Spring 笔记(二):AOP 与 JdbcTemplate 部分

三、AOP

1.AOP 的基本概念


AOP(Aspect-Oriented Programming)是一种编程范式,它将关注点分离出来,以便更好地管理代码。
在传统的面向对象编程中,关注点(如安全性、事务处理等)是混杂在业务逻辑中的。这使得代码难以理解和维护,因为关注点与业务逻辑交织在一起。,导致模块之间的耦合度过高。
AOP通过将关注点分离出来,将它们封装在称为“切面”的模块中,使得业务逻辑和关注点分离,从而提高了代码的可读性和可维护性。 在Java中,Spring框架提供了AOP的支持,可以通过在代码中使用特定的注解和配置来实现AOP编程。

1)通过登录的例子来理解 AOP

解析
上面的例子中,我们首先实现了一个通过数据库来验证登录是否成功的一个模块,但是这时候如果我们想要在运行的时候对这个用户进行权限判断,按照之前的编程思路,肯定是通过修改登录模块的源代码来实现的,可通过 AOP 可以实现不修改源代码而增加新的功能。

2.AOP 的底层原理


AOP 的底层用到了动态代理的技术,先来了解一下什么是动态代理:
动态代理是一种在运行时创建一个类和它的实例的技术。它是在运行时动态生成的类,通常是为了实现某些特定的功能。 动态代理通常使用Java的反射 API 来创建类和实例。反射API允许我们在运行时访问类和对象的信息,并且可以创建类的实例,调用类的方法和访问类的属性。
动态代理的一个常见用途是实现AOP(面向切面编程)。通过使用动态代理,可以将关注点(如安全性、事务处理等)封装在称为“切面”的模块中,使得业务逻辑和关注点分离,从而提高了代码的可读性和可维护性。

有两种情况,分别是有接口的情况下使用 JDK 动态代理,和没有接口的情况下使用 CGLIB 动态代理

3.AOP:JDK 动态代理


  1. 使用 JDK 动态代理,就需要 Proxy 这个类能提供给我们创建代理对象的方法。

    static Object newProxyInstance(ClassLoader loader,<?> interfaces, InvocationHandler h)
    //返回指定接口代理类的实例,该接口将方法调用分派给指定的调用处理程序
    

    ​ 有三个参数:类加载器、增强方法所在的类和这个实现的接口,支持多个接口、实现这个接口 InvocationHandler 创建代理对象,写增强的部分

  2. JDK 动态代理的实现

    public class UserTest {
        public static void main(String[] args) {
            Class[] classes = {UserInterface.class};
            User user = new User();
            UserProxy userProxy = new UserProxy(user);
            UserInterface o = (UserInterface)Proxy.newProxyInstance(User.class.getClassLoader(), classes, userProxy);
            o.add(1, 2);
    
        }
    }
    
    class UserProxy implements InvocationHandler {
        private Object obj;
        public UserProxy(Object obj) {
            this.obj = obj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("方法之前执行的");
            Object res = method.invoke(obj, args);
            System.out.println("方法之后执行的");
            return res;
        }
    }
    

    解析
    通过上面的代码,我们实现了代理类的创建和方法的调用,对接口 InvocationHandler 有更深的理解,它实现了方法并且能够在方法的前后去添加新的操作,这部分的代码属于底层原理,暂时不需要完全理解。

4.AOP 的常用术语


  1. 连接点:类里面的哪些方法是可以被增强的,这些方法称为连接点
  2. 切入点:实际上被增强的方法称为切入点
  3. 通知:实际增强的逻辑部分称为通知,通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知。
  4. 切面:将通知应用加入到切入点的过程,是一种动作。

5.AOP 操作:准备


  1. Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 并不是 spring 的组成部分,而是一个独立的 AOP 框架,一般把 AspectJ 和 Spring 框架一起使用,进行 AOP 操作。

  2. 基于 AspectJ 实现 AOP 操作,分为基于 xml 配置文件和注解方式,一般使用注解方式。

  3. 在项目工程中导入相关的依赖

  4. 在配置的阶段还需要了解切入点表达式,它可以帮助我们知道对哪个类中的哪个方法进行了增强。
    语法结构:

    execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])
    // 以下是一些举例
        // 对 com.atguigu.dao.BookDao类中的add方法进行增强
        execution(* com.atguigu.dao.BookDao.add(..))
        // 对 com.atguigu.dao.BookDao 类中的所有方法进行增强
        execution(* com.atguigu.dao.BookDao.*(..))
        // 对 com.atguigu.dao 所有类中的所有方法进行增强
        execution(* com.atguigu.dao.*.*(..))
        // * 表示所有的权限修饰符,返回类型可以省略
    

6.AOP 操作:AspectJ 注解


  1. 创建类,并且在类中定义方法

    @Component
    public class User{
        public int add(int a, int b) {
            System.out.println("add 方法执行");
            return (a + b);
        }
    }
    
  2. 创建增强类,增强类里面创建方法,让不同的方法代表不同的通知类型

    //注意一定需要上面的注解,要不然该类不会被构建
    @Component
    @Aspect
    public class UserProxy {
        @Before(value = "execution(* com.atguigu.spring5.User.add(..))")
        public void before() {
            System.out.println("前置通知");
        }
        @AfterReturning(value = "execution(* com.atguigu.spring5.User.add(..))")
        public void afterReturn(){
            System.out.println("后置通知(返回通知)");
        }
        @After(value = "execution(* com.atguigu.spring5.User.add(..))")
        public void after() {
            System.out.println("最终通知");
        }
        @AfterThrowing(value = "execution(* com.atguigu.spring5.User.add(..))")
        public void afterThrow() {
            System.out.println("异常通知");
        }
        @Around(value = "execution(* com.atguigu.spring5.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕之前");
            proceedingJoinPoint.proceed();
            System.out.println("环绕之后");
        }
    }
    
  3. 执行顺序

    环绕之前
    前置通知
    方法本身的执行
    环绕之后
    后置通知
    最终通知,return 之后
    

4. 进行通知的配置,在 spring 的配置文件中开启注册扫描
   创建 context 域,然后利用其中的 <context:component-scan base-package="com.atguigu"/> 来扫描

5. 配置不同类型的通知:已经在上面的代码中提前配好了

6. 配置开启代理对象的代码

   ```xml
   <aop:aspectj-autoproxy/>

经过上面的步骤,我们发现每个代理方法的前面都有一个相同的 value 值,有没有方法能够简化写法呢?

    @Pointcut(value = "execution(* com.atguigu.spring5.User.add(..))")
    public void pointDemo() {}
    @Before(value = "pointDemo()")
    public void before() {
        System.out.println("前置通知");
    }

通过在方法前面加上注解的方式来提取使用多次的方法,这样就可以简化写法。

再解决下一个细节,当有多个增强类对同一个方法进行增强的时候,如何设置优先级?

//在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{}

7.AOP 操作:配置文件

<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

<!--配置 aop 增强-->
<aop:config>
 <!--切入点-->
 <aop:pointcut id="p" expression="execution(*com.atguigu.spring5.aopxml.Book.buy(..))"/>
    
 <!--配置切面-->
 <aop:aspect ref="bookProxy">
     
 <!--增强作用在具体的方法上-->
 <aop:before method="before" pointcut-ref="p"/>
 </aop:aspect>
</aop:config>

四、JDBCTemplate

1.概述和准备工作


JDBCTemplate是Spring框架中的一个类,它是一个JDBC操作的模板类。JDBCTemplate封装了JDBC操作的大部分细节,使得开发人员可以更简单、更方便地进行数据库操作。
JDBCTemplate提供了各种方法,可以用于执行SQL语句、获取结果集、处理事务等。它还提供了一些方法,可以用于处理SQL语句的异常,例如execute()方法和update()方法。
使用JDBCTemplate可以大大简化JDBC操作,避免了许多JDBC操作中的常见错误,例如SQL注入、资源泄漏等。同时,JDBCTemplate还支持自动处理事务,可以大大简化事务处理的代码。

1)准备工作

引入相关的 jar 包

20231010193755333" style="zoom: 50%;" />

在 spring 配置文件中配置 spring 连接池

<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
 	<property name="url" value="jdbc:mysql:///user_db" />
 	<property name="username" value="root" />
 	<property name="password" value="123456" />
 	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>

配置 JdbcTemplate 对象,并向其中注入 dataSourse

	<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
 	<!--注入 dataSource-->
 	<property name="dataSource" ref="dataSource"></property>
</bean>

创建 dao 类,在 dao 中注入 JdbcTemplate 对 象

@Repository
public class BookDaoImpl implements BookDao {
 //注入 JdbcTemplate
 @Autowired
 private JdbcTemplate jdbcTemplate;
}

2.JdbcTemplate 实现数据库操作


1)实现数据库的添加操作

  1. 先来了解以下 jdbcTemplate 的 update方法:

    (int)			jdbcTemplate.update(String sql, Object[] args)
    // 返回的是影响的行数,Object 数组中存放的是 sql 语句中 ? 代表的数据
    // 它用于执行SQL更新操作(如INSERT、UPDATE、DELETE)
    
  2. 创建 dao 类和 service 类

    //这个注解表示他是连接数据库处理数据的组件
    @Repository
    public class BookDaoImpl implements BookDao {
         //注入 JdbcTemplate
         @Autowired
         private JdbcTemplate jdbcTemplate;
        
         //添加的方法
         @Override
         public void add(Book book) {
         //1 创建 sql 语句
         String sql = "insert into t_book values(?,?,?)";
         //2 调用方法实现
         Object[] args = {book.getUserId(), book.getUsername(), 
        		book.getUstatus()};
         int update = jdbcTemplate.update(sql,args);
         System.out.println(update);
         }
    
    @Test
    public void testJdbcTemplate() {
        ApplicationContext context =
        	new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        
        Book book = new Book();
        book.setUserId("1");
        book.setUsername("java");
        book.setUstatus("a");
        bookService.addBook(book);
    }
    

2)实现数据库的修改和删除操作

修改和删除操作也是只返回影响的行数的,我们仍可以用 update 方法完成上述的操作。

//1、修改
@Override
public void updateBook(Book book) {
    String sql = "update t_book set username=?,ustatus=? where user_id=?";
    Object[] args = {book.getUsername(), book.getUstatus(),book.getUserId()};
    int update = jdbcTemplate.update(sql, args);
    System.out.println(update);
}

//2、删除
@Override
public void delete(String id) {
    String sql = "delete from t_book where user_id=?";
    int update = jdbcTemplate.update(sql, id);
    System.out.println(update);
}

3)查询返回的是某个值

查询返回某个值即返回的是一行一列,可以使用到 queryForObject 方法去实现

(T)		queryForObject(String sql, Object[] args, Class<T> requiredType)
	// 这个方法有三个个参数,分别是执行的 sql 语句和传入 ? 的参数
    // 以及需要返回的值的 class 值 

下面来看具体的实现,我们希望返回表中记录的总条数,可以知道返回的是一个数据。

public int selectCount() {
	String sql = "select count(*) from t_book";
	Integer count = jdbc.queryForObject(sql, Integer.class);
	return count;
}

4)查询返回对象

如果返回的是一个对象的话则需要指定返回的的类型。

先来介绍一下其中的 queryForObject 方法

(T)		queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
	// 先来解析以下其中的三个属性
    // 先传入要执行的 sql 语句,这里执行的是返回一个对象的操作
    // 第二个参数是一个 RowMapper 对象,这是一个接口定义了一个方法,可以将返回的结果映射为一个对象。
    // 第三个参数是 ? 中需要传入的数据
    // 则通过上面的方法可以实现返回一个对象,通过 rowMapper 来将返回的结果映射为对象
// 实现了通过 id 来返回对象的方法
@Override
public Book findBookInfo(String id) {
    String sql = "select * from t_book where user_id=?";
    // 调用方法
    Book book = jdbcTemplate.queryForObject(sql, 
    new BeanPropertyRowMapper<Book>(Book.class), id);
    return book;
}

5)查询返回集合

先来看一个应用场景:在图书管理系统中,我们需要通过 limit 语句限制查询的返回数来达到分页的功能,这时候,就需要返回一个 book 的 list 对象。

(T)		query(String slq, RowMapper<T> rwoMapper, Object... args)
    // 这里有三个返回的参数,具体的效果和查询返回对象的时候是相同的,但方法是 query,可以返回一个集合
//查询返回集合
@Override
public List<Book> findAllBook() {
    String sql = "select * from t_book";
    //调用方法,这里的 BeanPropertyMapper 是 MapperRow 接口的实现对象,用于将返回结果映射为对象
    List<Book> bookList = jdbcTemplate.query(sql,
    	new BeanPropertyRowMapper<Book>(Book.class));
    return bookList;
}

6)实现批量添加

批量添加可以提高数据库添加大量数据的速度,spring 中也定义了响应的方法去实现

int 	batchUpdate (String sql, List<Object> batchArgs)
	// 有两个参数
	// 第一个参数是一个 sql 语句
	// 第二个参数是一个 list 集合,添加多条记录的数据
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
    String sql = "insert into t_book values(?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

public static void main(String[] args) {
    //批量添加测试
	List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"3","java","a"};
    Object[] o2 = {"4","c++","b"};
    Object[] o3 = {"5","MySQL","c"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    //调用批量添加
    bookService.batchAdd(batchArgs);	
}

7)实现批量修改的操作

//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
    // 批量修改操作返回的是一个数组类型,表示每次操作影响的行数
	String sql = "update t_book set username=?,ustatus=? where user_id=?";
	int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
	System.out.println(Arrays.toString(ints));
}

8)实现批量删除的操作

// 批量删除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
	String sql = "delete from t_book where user_id=?";
	int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
	System.out.println(Arrays.toString(ints))
}

// 测试方法
public static void main(String[] args) {
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"3"};
    Object[] o2 = {"4"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    bookService.batchDeleteBook(batchArgs);  
}
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*Soo_Young*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值