Spring AOP
AOP,面向切面编程,当你想对某个类中的某个方法进行添加功能但又不想对源码进行修改,这个时候就可以用到AOP了;
注解实现AOP
先配置xml文件,将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: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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
<!--开启注解开发aop-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
然后在你要的那个切面上注解
@Component
@Aspect
public class LogerPage {
}
再写一个切点,切点是一个你将要扩展功能的那个方法的位置用一个公式表达出来如下图:
@Pointcut(value = "execution(* com.yjxxt.service.UserServiceImpl.*(..))")
public void cut(){
}
然后再写入通知,通知是你要扩展的功能,需要扩展的方法体:
//前置通知
@Before(value = "cut()")
public void before(){
System.out.println(new Date().toLocaleString()+"Before");
}
//最终通知
@After(value = "cut()")
public void after(){
System.out.println(new Date().toLocaleString()+"After");
@AfterReturning(value = "cut()")
public void afterReturn(){
System.out.println("afterReturn ....");
}
@AfterThrowing(value = "cut()",throwing="e")
public void afterThrowing(Exception e){
System.out.println(e.getCause()+"---->"+e.getMessage());
System.out.println(new Date().toInstant());
}
@Around(value = "cut()")
public Object around(ProceedingJoinPoint pjp){
//执行目标方法
Object obj = null;
try {
System.out.println("Before....");
obj = pjp.proceed();
System.out.println("After.....");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知了");
}
System.out.println("通知一一下");
//返回结果
return obj;
}
xml文件实现AOP
<bean id="uc" class="com.yjxxt.controller.UserController">
<property name="userService" ref="us"></property>
</bean>
<bean id="us" class="com.yjxxt.service.UserServiceImpl">
<property name="userDao" ref="ud"></property>
</bean>
<bean id="ud" class="com.yjxxt.dao.UserDaoImpl"></bean>
<bean id="cut" class="com.yjxxt.aop.LogerPage"></bean>
<!--aop的配置-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="cut">
<!--切点-->
<aop:pointcut id="pc" expression="execution(* com.yjxxt.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pc"></aop:before>
</aop:aspect>
</aop:config>
AOP实现原理-代理模式
代理模式,简单来说,两个类实现了同一个接口,一个代理对象,一个委托对象,委托对象不修改源码,让代理对象执行方法,代理对象执行目标对象没有的方法,如果执行的方法委托对象有的话就执行目标对象有的方法
生活中的例子如:婚庆公司,婚你结,准备工作和结束工作婚庆公司搞
静态代理
自己创建代理对象,缺点容易类爆炸,不灵活
/**
* 定义行为
*/
public interface Marry {
public void toMarry();
}
实现接口:一个委托对象
/**
* 静态代理 ——> 目标对象
*/
public class You implements Marry {
// 实现行为
@Override
public void toMarry() {
System.out.println("我要结婚了...");
}
}
一个代理对象
/**
* 静态代理 ——> 代理对象
*/
public class MarryCompanyProxy implements Marry {
// 目标对象
private Marry marry;
// 通过构造器将目标对象传入
public MarryCompanyProxy(Marry marry) {
this.marry = marry;
}
// 实现行为
@Override
public void toMarry() {
// 增强行为
before();
// 执行目标对象中的方法
marry.toMarry();
// 增强行为
after();
}
/**
* 增强行为
*/
private void after() {
System.out.println("新婚快乐,早生贵子!");
}
/**
* 增强行为
*/
private void before() {
System.out.println("场地正在布置中...");
}
}
动态代理
两种实现方式: JDK 动态代理和CGLIB动态代理
动态代理的特点:
- 目标对象不固定
- 在应用程序执行时动态创建目标对象
- 代理对象会增强目标对象的行为
JDK动态代理
缺点:未实现接口的类的对象不能获得代理对象
public class JdkHandler implements InvocationHandler {
// 目标对象
private Object target; // 目标对象的类型不固定,创建时动态生成
// 通过构造器将目标对象赋值
public JdkHandler(Object target) {
this.target = target;
}
/**
* 1、调用目标对象的方法(返回Object)
* 2、增强目标对象的行为
* @param proxy 调用该方法的代理实例
* @param method 目标对象的方法
* @param args 目标对象的方法形参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
// 增强行为
System.out.println("==============方法前执行");
// 调用目标对象的方法(返回Object)
Object result = method.invoke(target,args);
// 增强行为
System.out.println("方法后执行==============");
return result;
}
/**
* 得到代理对象
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* loader:类加载器
* interfaces:接口数组
* h:InvocationHandler接口 (传入InvocationHandler接口的实现类)
*
*
* @return
*/
public Object getProxy() {
return
Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInt
erfaces(),this);
}
}
CGLIB动态代理
在pom.xml导入依赖
优点:不管是实现了接口的类或是没有实现接口的类的对象都可以获得代理对象
缺点:final修饰的类的对象不可以获得代理对象
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class CglibInterceptor implements MethodInterceptor {
// 目标对象
private Object target;
// 通过构造器传入目标对象
public CglibInterceptor(Object target) {
this.target = target;
}
/**
* 获取代理对象
* @return
*/
public Object getProxy() {
// 通过Enhancer对象的create()方法可以生成一个类,用于生成代理对象
Enhancer enhancer = new Enhancer();
// 设置父类 (将目标类作为其父类)
enhancer.setSuperclass(target.getClass());
// 设置拦截器 回调对象为本身对象
enhancer.setCallback(this);
// 生成一个代理类对象,并返回
return enhancer.create();
}
/**
* 拦截器
* 1、目标对象的方法调用
* 2、增强行为
* @param object 由CGLib动态生成的代理类实例
* @param method 实体类所调用的被代理的方法引用
* @param objects 参数值列表
* @param methodProxy 生成的代理类对方法的代理引用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object object, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
// 增强行为
// 调用目标对象的方法(返回Object)
Object result = methodProxy.invoke(target,objects);
// 增强行为
System.out.println("方法后执行==============");
return result;
}
}