接着上面的内容。
今天主要学习的是AOP
一、什么是AOP?
Aspect-Oriented Programming, 面向切面编程。
举个例子,我们去市场买菜,为了防止商家卖的菜有农药残留,我们需要随身带一个校测器,每买一个菜我们都需要自己手动去检查。当我们使用了AOP之后,我们只需要去买菜,AOP自动帮我们完成了检测是否有农药的残留。
到我们的Javabean中,我们要操作一个方法,比喻添加用户,在添加用户之前我们需要校验权限,在添加用户后我们需要做一个事后日志记录,如果每个方法我们都需要这么做的话,将会是一个很大的工程,当某天我们不需要这个这个权限的时候,又需要一个个删除,想想就快崩溃了。那么我们有没有将权限校验和时候日志记录抽取出来。AOP就给我们提供了这样的方法。
二、使用AOP的好处
1、日志记录的代码和真正的业务逻辑代码进行分离
2、通用的系统功能(日志记录、权限校验)进行了高度的模块化
3、业务逻辑的功能变的更简洁,仅仅包含业务逻辑的代码
4、AOP可以将系统功能(日志记录)与业务逻辑功能搅和到一起执行
三、AOP的两种底层实现方式
1、动态代理
1.1 什么是代理
代理设计模式的原理: 使用一个代理对象将原始对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理对象. 代理对象决定是否以及何时将方法调用转到原始对象上。
1.2 静态代理
@Override public boolean update(User user) { System.out.println("执行权限校验,日志记录......."); return userService.update(user); }
为每一个类手动添加代理方式,如果类很多,将会是一个很庞大的工程,因此我们采用动态代理的方式
1.3 动态代理的好处
我们可以在系统运行时,动态生成一个持有原始对象,并实现代理接口的Proxy,同时 “植入”我们的通用逻辑(日志、权限等)。
动态代理可以实现静态代理相同的功能,唯一的区别这些Proxy的创建都是自动的并且在系统运行时生成的。这样就不需要对每一个原始对象来创建一个代理了。
接下来我们看动态代理的两种实现方式:JDK动态代理和CGLIB动态代理
2、JDK动态代理
2.1创建maven工程,导包
<dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.2.4.RELEASE</version> </dependency>
2.2 定义接口和实现类
public interface UserService { public String getbyid(); public void add(); public void delete(); public void update(); public void batch(); }
public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("执行具体的业务逻辑:add......"); } @Override public String getbyid() { System.out.println("执行具体的业务逻辑:getbyid......"); return null; } @Override public void delete() { System.out.println("执行具体的业务逻辑:delete......"); } @Override public void update() { System.out.println("执行具体的业务逻辑:update......"); } @Override public void batch() { System.out.println("执行具体的业务逻辑:batch......"); } }
2.3、定义逻辑增强(切面类:封装增强逻辑)
public class SecurityAspect { //校验逻辑 public void check(){ System.out.println("校验权限............................"); } }
2.4、对原始对象创建他的代理对象
//创建代理对象 public class ProxyFactory implements InvocationHandler { // 目标对象 private Object msg; public ProxyFactory(Object msg) { super(); this.msg = msg; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 获取切面类并执行切面增强 SecurityAspect securityAspect = new SecurityAspect(); securityAspect.check(); // 执行目标对象方法 Object invoke = method.invoke(msg, args); return invoke; } // 创建代理对象 public Object getproxy() { return Proxy.newProxyInstance(msg.getClass().getClassLoader(), msg.getClass().getInterfaces(), this); } }
2.5、创建测试类
@Test public void testName2() throws Exception { UserService userService = new UserServiceImpl(); UserService user = (UserService) new ProxyFactory(userService).getproxy(); user.add(); }
2.6存在的问题
使用内置的Proxy(JDK动态代理)实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。
3、CGLIB动态代理
cglib继承被代理的类(UserServiceImpl),重写方法,织入通知,动态生成字节码并运行,因为是继承所以final类是没有办法动态代理的。
3.1定义我们的目标类(不需要实现接口)
public class UserCglib { public void show(String string){ System.out.println("目标类方法"+string); } }
3.2、定义我们的切面
public class UserAspct { public void writelog(){ System.out.println("我是切面控制对象的"); } }
3.3、定义动态代理
public class CglibPorxyFactory implements MethodInterceptor { //目标类 private Object msg; //逻辑增强 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable { //获取拦截类,执行拦截方法 UserAspct userAspct = new UserAspct(); userAspct.writelog(); //获取目标类,执行目标方法 Object invokeSuper = proxy.invokeSuper(o, objects); return invokeSuper; } //获取代理对象 public Object getproxy(Object msg) { this.msg=msg; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(msg.getClass()); enhancer.setCallback(this); Object create = enhancer.create(); return create; } }
3.4、测试类
@Test public void testName() throws Exception { UserCglib getproxy = (UserCglib) new CglibPorxyFactory().getproxy(new UserCglib()); getproxy.show("===================>小泽"); }
四、AOP 中的专业术语
切面(aspect):横切逻辑被模块化的特殊对象。即它是一个类 :如LogAspect
通知(advice):切面中必须完成的工作。即它是类中的一个方法:如writeLog()
目标类(target):被通知增强的对象
代理类(proxy):向目标类应用通知增强之后产生的对象
切入点(pointcut):切面中通知执行的“地点”的定义
连接点(JoinPoint): 与切入点匹配的执行点:如目标类中的所有方法getUserId()
五、AOP基于xml配置方式实现
1、Spring基于xml开发AOP
1.1创建接口及实现类
public interface UserService { public String getbyid(); public void add(); public String delete(); public void update(); public void batch(); }
public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("执行具体的业务逻辑:add......"); } @Override public String getbyid() { System.out.println("执行具体的业务逻辑:getbyid......"); // System.out.println(1/0); return null; } @Override public String delete() { System.out.println("执行具体的业务逻辑:delete......"); return "大数据"; // System.out.println(1/0); } @Override public void update() { System.out.println("执行具体的业务逻辑:update......"); // System.out.println(1/0); } @Override public void batch() { System.out.println("执行具体的业务逻辑:batch......"); } }
1.2、定义切面类
public class XmlAspect { public void beforAspect(JoinPoint jp ){ System.out.println(jp.getSignature().getName()); System.out.println("我是前置,我要先执行........................."); } public void afterAspect(){ System.out.println("我是后置办法,我要后执行........................."); } public void afterReturningAspect(JoinPoint jp,Object obj){ System.out.println(obj); System.out.println("我是返回办法,不管对错我都要执行........................"); } public void exceptionAspect(Exception ex){ System.out.println(ex); System.out.println("我是报错,有错我就执行........................."); } public Object aroundAspect( ProceedingJoinPoint jp ) throws Throwable{ System.out.println("我是环绕通知前........................."); Object proceed = jp.proceed(); System.out.println("我是环绕通知后........................."); return proceed; } }
1.3 在Spring容器中配置目标类、切面类、配置核心文件
<?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:c="http://www.springframework.org/schema/c" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 注册目标及切面对象 --> <bean id="userserv" class="cn.itcast.dao.xml.UserServiceImpl"></bean> <bean id="xmlaspect" class="cn.itcast.dao.xml.XmlAspect"></bean> <!-- 配置spring核心配置文件:配置切入点、通知、切面 --> <aop:config> <!-- 切入点配置 --> <aop:pointcut expression="execution(* *.add*(..))" id="atter1" /> <aop:pointcut expression="execution(* *.getbyid*(..))" id="atter2" /> <aop:pointcut expression="execution(* *.delete*(..))" id="atter3" /> <aop:pointcut expression="execution(* *.update*(..))" id="atter4" /> <aop:pointcut expression="execution(* *.batch*(..))" id="atter5" /> <!-- 切面配置 --> <aop:aspect ref="xmlaspect"> <!-- 前置通知配置 --> <aop:before method="beforAspect" pointcut-ref="atter1" /> <!-- 后置通知 --> <aop:after method="afterAspect" pointcut-ref="atter2" /> <!-- 返回通知 --> <aop:after-returning method="afterReturningAspect" pointcut-ref="atter3" returning="obj" /> <!-- 报错通知 --> <aop:after-throwing method="exceptionAspect" pointcut-ref="atter4" throwing="ex" /> <!-- 环绕通知 --> <aop:around method="aroundAspect" pointcut-ref="atter5" /> </aop:aspect> </aop:config> </beans>
1.4 测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext-aop-xml.xml") public class Xmltest { @Autowired//在spring容器中获取目标代理对象 private UserService service; @Test public void testName() throws Exception { service.add(); // service.getbyid(); // service.delete(); // service.update(); // service.batch(); } }
六、AOP基于注解方式的实现
1、开启spring注解扫描及开启aop注解的自动代理
<?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:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 扫描注解 --> <context:component-scan base-package="cn.itcast.spring.annotation"></context:component-scan> <!-- 开启自动代理 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
2、定义接口和实现类
public interface UserService { public String getbyid(); public void add(); public String delete(); public void update(); public void batch(); }
@Service//将这个类放在Spring容器中 public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("执行具体的业务逻辑:add......"); } @Override public String getbyid() { System.out.println("执行具体的业务逻辑:getbyid......"); // System.out.println(1/0); return null; } @Override public String delete() { System.out.println("执行具体的业务逻辑:delete......"); // System.out.println(1/0); return "大数据"; } @Override public void update() { System.out.println("执行具体的业务逻辑:update......"); // System.out.println(1/0); } @Override public void batch() { System.out.println("执行具体的业务逻辑:batch......"); } }
3、定义切面类
@Component @Aspect public class AspectaAnnotation { @Before(value = "execution(* *.add*(..))") public void beforAspect(JoinPoint jp ){ System.out.println(jp.getSignature().getName()); System.out.println("我是前置,我要先执行........................."); } @After(value = "execution(* *.getbyid*(..))") public void afterAspect(){ System.out.println("我是后置办法,不管对错我都要执行执行........................."); } @AfterReturning(value="execution(* *.delete*(..))",returning="obj" ) public void afterReturningAspect(JoinPoint jp,Object obj){ System.out.println(obj); System.out.println("我是返回办法,........................"); } @AfterThrowing(value="execution(* *.update*(..))",throwing="ex") public void exceptionAspect(Exception ex){ System.out.println(ex); System.out.println("我是报错,有错我就执行........................."); } @Around(value = "execution(* *.batch*(..))") public Object aroundAspect( ProceedingJoinPoint jp ) throws Throwable{ System.out.println("我是环绕通知前........................."); Object proceed = jp.proceed(); System.out.println("我是环绕通知后........................."); return proceed; } }
4、测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext-aop-annotation.xml") public class Annotationtest { @Autowired private UserService service; @Test public void testName() throws Exception { // service.add(); // service.getbyid(); // service.delete(); // service.update(); service.batch(); } }