上一篇:15-Spring AOP的底层实现原理JDKProxy&CGLIB https://blog.csdn.net/fsjwin/article/details/109481104
前面我们介绍的aop编程完全可以满足项目的需要,但是有没有更为简便的方式呢?
当然有了,就是aspectJ为我们提供的大餐,使用注解的aop编程
下面就开启我们的大仓之旅
值得注意的是不管是aop的注解编程,还是在xml中就行配置,他们的主要矛盾是一样的,矛盾的主要方面也是一样的。即
- 目标对象
- 增强功能
- 切入点
- 编制切面(切入点和增强功能整合在一起)
1.开发过程
- MyAspctJ .java 切面类(包含了增强功能、切入点、编制切面)
package aspectJ;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
* @author yuhl
* @Date 2020/11/4 6:52
* @Classname MyAspctJ
* @Description 通过注解的方式aop开发
* 但是这四样一样不能少
* 1. 目标对象 UserServiceImpl
* 2. 增强功能
* 3. 切入点
* 4. 编制切面(切入点和增强功能整合在一起)
*/
@Aspect //@Aspect告诉spring我是一个切面类
public class MyAspctJ {
//1. 目标对象 UserServiceImpl
UserService userService = new UserServiceImpl();
/**
* 切面, 内含2. 增强功能 和 3. 切入点
* 2. 增强功能
* 3. 切入点
* 4. 编制切面(切入点和增强功能整合在一起)
*/
@Around("execution(* login(..))") //3. 切入点
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object res = null;
try {
System.out.println("MyAspctJ.around 前**********" ); //2. 增强功能
res = proceedingJoinPoint.proceed();
System.out.println("MyAspctJ.around 后**********" ); //2. 增强功能
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return res;
}
}
- 配置文件,主要不是0配置哦!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="aspectJ.UserServiceImpl"/>
<!--注解的aop编程 这个MyAspctJ还是要告诉spirng的-->
<bean id="aspectj" class="aspectJ.MyAspctJ"/>
<!--告诉spring开始注解的aop编程-->
<aop:aspectj-autoproxy/>
</beans>
- 测试
/**
* 基于主机的aop编程
*/
@Test
public void test6() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext8.xml");
aspectJ.UserService userService = (aspectJ.UserService)ctx.getBean("userService");
userService.login("zhangsan","111111");
userService.register(new User(2, "222222"));
}
- 结果
MyAspctJ.around 前**********
UserServiceImpl.login
MyAspctJ.around 后**********
UserServiceImpl.register
-
总结
还是4个核心组件,一个不少,最终达到了想要的效果。这就是aspectj这个封装框架的强大之处。 -
进价
想想一下,如果我的切面中有n多的方法均要使用这个切面怎办,如下:
有没有看到代码冗余呢?有的。如果我们有999999个方法,那么这99999个方法的上面均要写上
@Around("execution(* login(..))") //3. 切入点
那还不翻天了。有一天我们要修改这个切入点,要修改的黄花菜都凉了,怎么办。
对我们可以把他声明在一个地方,其他想用他的地方去引用不就好了。
看下面:
这样就完美解决这个问题。
具体代码入下:
import base.comobjectcreate.factorybean.MyConnectionFacoryBean;
import base.domain.*;
import base.factory.BeanFactory;
import base.lifecycle.Product;
import base.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import proxy.service.OrderService;
import proxy.service.User;
import proxy.service.UserService;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author yuhl
* @Date 2020/10/31 14:05
* @Classname TestDemo
* @Description TODO
*/
public class TestDemo2 {
/**
* 静态代理
*/
@Test
public void test1() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext5.xml");
UserService userService = (UserService)ctx.getBean("userService");
userService.login("zhangsan","111111");
userService.regester(new User(2, "222222"));
}
@Test
public void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext5.xml");
OrderService orderService = (OrderService)ctx.getBean("orderService");
orderService.order();
}
/**
* spring动态代理
*/
@Test
public void test3() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml");
OrderService orderService = (OrderService)ctx.getBean("orderService");
orderService.order();
}
@Test
public void test4() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml");
UserService userService = (UserService)ctx.getBean("userService");
userService.login("zhangsan","111111");
userService.regester(new User(2, "222222"));
}
@Test
public void test5() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext7.xml");
proxy.service.UserService userService = (proxy.service.UserService)ctx.getBean("userService");
userService.login("zhangsan","111111");
userService.regester(new User(2, "222222"));
}
/**
* 基于主机的aop编程
*/
@Test
public void test6() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext8.xml");
aspectJ.UserService userService = (aspectJ.UserService)ctx.getBean("userService");
userService.login("zhangsan","111111");
userService.register(new User(2, "222222"));
}
}
测试结果
MyAspctJ.around 前**********
MyAspctJ.logAround 前**********
UserServiceImpl.login
MyAspctJ.logAround 后**********
MyAspctJ.around 后**********
UserServiceImpl.register
测试通过
7. jdkProxy和cgLib怎么选
默认情况下使用的是jdkproxy,证据如下。
如何切换默认使用cglib呢?
- 在注解aop开发中切换十分简单:
<!--告诉spring开始注解的aop编程-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
添加proxy-target-class="true"即可,结果如下:
- 如果使用之前的xml进行aop的开发怎么切换呢?想先也知道也是一个配置的事,但是写在哪里呢?
配置如下:
也是这句话,只是放在了aop:config标签中:
<aop:config proxy-target-class="true">
2.AOP编程的坑
可以参考:Spring中同一类@Transactional修饰方法相互调用的坑https://blog.csdn.net/fsjwin/article/details/109211355
userService中register()方法调用login()方法的时候,只调用哪个的是this.login()并不是userServiceProxy的login()方法。
- 问题现象
测试:
/**
* 基于主机的aop编程
*/
@Test
public void test6() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext8.xml");
aspectJ.UserService userService = (aspectJ.UserService)ctx.getBean("userService");
//userService.login("zhangsan","111111");
userService.register(new User(2, "222222"));
}
运行结果:
UserServiceImpl.register
UserServiceImpl.login
这并不是我们想要的哦!我们想要的是能够对login()方法增强。
2. 问题解决
通过以上四部可以获得工厂,然后通过工厂再次获得代理对象,使用代理对象对login()方法进行调用即可。
UserServiceImpl.java
package aspectJ;
import base.dao.UserDao;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import proxy.service.User;
/**
* @author yuhl
* @Date 2020/10/31 14:07
* @Classname userServiceImpl
* @Description TODO
*/
public class UserServiceImpl implements UserService, ApplicationContextAware {
ApplicationContext ctx = null;//获取项目启动的时候创建的那个工厂,从工厂中获得代理即可。
@Override
public boolean login(String username, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("于红亮", "ioewrioew");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
}
结果:
UserServiceImpl.register
MyAspctJ.around 前**********
MyAspctJ.logAround 前**********
UserServiceImpl.login
MyAspctJ.logAround 后**********
MyAspctJ.around 后**********
3.AOP总结
一图在手,总结所有:
下一篇:17-Spring持久层框架整合https://blog.csdn.net/fsjwin/article/details/109485779