Spring二刷笔记-AOP概念理解与实现
Posted 滑稽404#
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring二刷笔记-AOP概念理解与实现相关的知识,希望对你有一定的参考价值。
文章目录
AOP(面向切面):在不更改原有代码的情况下,添加附属功能
如:
操作数据的CRUD功能,给他加上日志,无论日志的开关,CRUD都能够正常执行,影响不到他
给登录功能加上权限模块,权限的开关都不影响用户登录
一、实现原理
动态代理:
- JDK动态代理:创建接口实现类代理对象,增强类的方法
- GCLIB动态代理:创建子类代理对象,增强类的方法
以下实现方式是基于JDK动态代理实现↓
生成代理对象基本实现
UserDao daoProxy=(UserDao)
Proxy.newProxyInstance(
currentClass.class.getClassLoader(),//当前类加载器
dao.getClass().getInterfaces(),//增强对象所在类的接口
new DaoInvocation(dao)//InvocationHandler,创建代理对象,里面写增强方法
);
//DaoInvocation是实现了InvocationHandler的类
(UserDao) daoProxy=(UserDao)Proxy.newProxyInstance()
(UserDaoImpl) daoProxy=(UserDaoImpl)Proxy.newProxyInstance()
千万不能强转为实现类
1.没有通过IOC获取对象的动态代理
public class DaoProxy {
public static void main(String[] args) {
//Class[] interfaces={UserDao.class};//增强对象所在类的接口
UserDao dao=new UserMapper();
UserDao daoProxy=(UserDao)
Proxy.newProxyInstance(
DaoProxy.class.getClassLoader(),//当前类加载器
//UserDao.class.getInterfaces(),
dao.getClass().getInterfaces(),//增强对象所在类的接口
//interfaces,
new DaoInvocation(dao)//代理对象,里面写增强方法
);
//代理实现add方法
Object res=daoProxy.add(3,5);
System.out.println(res);
}
}
class DaoInvocation implements InvocationHandler {
//代理的对象
Object obj;
public DaoInvocation(Object obj){
this.obj=obj;
}
//增强逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前:方法名:"+method.getName()+" 参数:"+ Arrays.toString(args));
Object result=method.invoke(obj,args);//实现方法
System.out.println("方法执行后:"+result.toString());
return result;
}
}
2.通过IOC获取对象的动态代理
public class UserServiceProxy {
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("resource/bean2.xml");
UserService userService = context.getBean("userService",UserService.class);
//UserService userService=new UserServiceImpl();
// int x=userService.add(3,4);
// System.out.println(x);
Class[] interfaces={UserService.class};
UserService userServiceProxy=(UserService)
Proxy.newProxyInstance(
UserServiceProxy.class.getClassLoader(),//当前类加载器
userService.getClass().getInterfaces(),//增强对象类所在的接口
new UserServiceInvocation(userService));//代理对象
Object res=userServiceProxy.add(3,5);
System.out.println("代理得到的结果:"+res);
}
}
class UserServiceInvocation implements InvocationHandler{
Object obj;
public UserServiceInvocation(Object obj){
this.obj=obj;
}
//增强逻辑实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前:方法名:"+method.getName()+" 参数:"+ Arrays.toString(args));
System.out.print("执行:");
Object result=method.invoke(obj,args);
System.out.println("方法执行后:"+result.toString());
return result;
}
}
二、AOP专业术语
- 连接点:能够增强的方法,如CRUD都能够被增强
- 切入点:实际增强的方法,如添加操作被增强了,其他没增强,那么添加方法就是切入点,其他的不是
- 通知(增强):增强方法的逻辑部分(逻辑代码)
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知 - 切面:把通知应用到切入点的过程
三、具体实现
1.相关依赖:aop和aspectjweaver
需要aop和aspectjweaver一起做切面操作
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<!--切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
2.切点表达式
excution([权限修饰符] [返回类型] [类路径] [方法名称] [参数列表])
*表示所有 返回类型可以省略
excution(* com.chime.dao.add(…))
对任意权限的com.chime.dao类中的add方法进行增强
excution(* com.chime.dao.*(…))
对任意权限的com.chime.dao里的所有方法进行增强
3.注解实现aop
(1)添加命名空间和打开配置
添加context命名空间打开注解扫描
添加aop命名空间打开自动代理
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.chime"/>
<!--自动为@Aspect创建代理-->
<aop:aspectj-autoproxy/>
</beans>
(2)用注解实现切面通知
@Component
@Aspect//声明切面,创建代理对象
public class UserDaoProxy {
//前置通知
@Before("execution(* com.chime.dao.UserDao.add(..))")
public void before(){
System.out.println("before");
}
//最终通知
@After("execution(* com.chime.dao.UserDao.add(..))")
public void after(){
System.out.println("after");
}
//后置通知
@AfterReturning("execution(* com.chime.dao.UserDao.add(..))")
public void afterReturning(){
System.out.println("AfterReturning");
}
//异常通知
@AfterThrowing("execution(* com.chime.dao.UserDao.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing");
}
//环绕通知
@Around("execution(* com.chime.dao.UserDao.add(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前");
Object result=proceedingJoinPoint.proceed();
System.out.println("环绕后");
return result;
}
}
after无论如何会实现,如果有异常,会进行异常通知,但是后置通知不会出现
(3)提取公共切入点
@Pointcut(value = "execution(* com.chime.dao.UserDao.add(..))")
public void daoPoint(){
}
//前置通知
@Before(value="daoPoint()")//就可以直接通过公共切入点写通知了
public void before(){
System.out.println("before");
}
(4)多个增强类,设置增强优先级
添加@Order()注解,值越低优先级越高
@Component
@Aspect//声明切面,创建代理对象
@Order(3)
public class UserDaoProxy {
}
@Component
@Aspect
@Order(1)
public class PersonProxy {
}
PersonProxy的Order为1比UserDaoProxy的优先级高,先执行
2.xml实现aop
纯xml配置,不需要开启扫描和自动代理
public class Buy {
public void buy(){
System.out.println("买东西");
}
}
public class BuyProxy {
public void before(){
System.out.println("before");
}
}
<bean id="buy" class="com.chime.dao.Buy"></bean>
<bean id="buyProxy" class="com.chime.proxy.BuyProxy"></bean>
<aop:config>
<!--配置切入点-->
<aop:pointcut id="buyPoint" expression="execution(* com.chime.dao.Buy.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="buyProxy">
<aop:before method="before" pointcut-ref="buyPoint"/>
</aop:aspect>
</aop:config>
以上是关于Spring二刷笔记-AOP概念理解与实现的主要内容,如果未能解决你的问题,请参考以下文章