aop是什么
我们将记录日志功能解耦为日志切面,它的目标是解耦。进而引出AOP的理念:就是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中!
著作权归https://pdai.tech所有。
AOP 有哪些应用场景?
举几个例子:
- 记录日志(调用方法后记录日志)
- 监控性能(统计方法运行时间)
- 权限控制(调用方法前校验是否有权限)
- 事务管理(调用方法前开启事务,调用方法后提交关闭事务 )
- 缓存优化(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )
先来看一下代理模式(静态代理)
代理模式包含如下角色:
- Subject: 抽象主题角色
- Proxy: 代理主题角色
- RealSubject: 真实主题角色
模式动机
在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
代码分析
/**
* 抽象主题角色
*/
public interface AopService {
public void aopTest();
}
/**
* 真实主题真实
*/
public class AopServiceImpl implements AopService {
public void aopTest() {
System.out.println("保存");
}
}
import com.xx.job.jdkProxy.AopService;
import com.xx.job.jdkProxy.AopServiceImpl;
/**
* 代理主题角色
*/
public class AopAspectjServiceImpl implements AopService {
private AopServiceImpl aopService;
public AopAspectjServiceImpl(){}
public AopAspectjServiceImpl(AopServiceImpl aopService){
this.aopService = aopService;
}
@Override
public void aopTest() {
System.out.println("开始事务");
aopService.aopTest();
System.out.println("提交事务");
}
}
测试
import com.xx.job.jdkProxy.AopServiceImpl;
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
AopServiceImpl aopService = new AopServiceImpl();
AopAspectjServiceImpl aopAspectjService = new AopAspectjServiceImpl(aopService);
aopAspectjService.aopTest();
}
}
输出结果:
开始事务
保存
提交事务
代理模式的优点
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
- 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 保护代理可以控制对真实对象的使用权限。
代理模式的缺点
- 由于在客户端和真实主题之间增加了代理对象,因此 有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。
JDK代理(动态代理)
代理特点
必须实现接口
java.lang.reflect包下的Proxy类进行代理
代码实现
抽象角色和真实角色跟上面是一样的,
代理角色:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object object;
public ProxyFactory(){
}
public ProxyFactory(Object object){
this.object = object;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务");
Object invoke = method.invoke(object, args);
System.out.println("提交事务");
return invoke;
}
});
}
}
测试:
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
AopServiceImpl aopService = new AopServiceImpl();
AopService proxyInstance = (AopService)new ProxyFactory(aopService).getProxyInstance();
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true" );
proxyInstance.aopTest();
proxyInstance.aopTest();
}
}
测试结果:
开始事务
保存
提交事务
Cjlib代理(动态代理)
代理特点:无需实现接口,但是不能用final修饰
代码实现
/**
* 被代理类
*/
public class AopServiceImpl {
public void aopTest() {
System.out.println("保存");
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cjlib代理工厂
*/
public class CjlibProxyFactory implements MethodInterceptor {
/**
* obj——“this”,增强对象方法——拦截方法args——参数数组;原始类型被包装代理 - 用于调用超级(非拦截方法);可以根据需要多次调用
* @param o obj——“this”
* @param method 增强对象方法——拦截方法args——参数数组
* @param objects 原始类型被包装代理
* @param methodProxy 用于调用超级(非拦截方法),可以根据需要多次调用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("事务开始");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("事务提交");
return invoke;
}
}
测试
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
// 创建cjlib工厂
CjlibProxyFactory cjlibProxyFactory = new CjlibProxyFactory();
// 创建增强类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(AopServiceImpl.class);
// 设置工厂回调
enhancer.setCallback(cjlibProxyFactory);
// 创建代理对象
AopServiceImpl o = (AopServiceImpl)enhancer.create();
// 调用代理方法
o.aopTest();
}
}
结果
事务开始
保存
事务提交
springaop代理实现
导入maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建接口
public interface Animal {
public void eat();
}
创建实现类
import org.springframework.stereotype.Service;
@Service
public class Dog implements Animal{
@Override
public void eat() {
System.out.println("吃骨头");
}
@SysLog("看门")
public void lookHouse() {
System.out.println("看门");
}
}
import org.springframework.stereotype.Service;
@Service
public class Pig implements Animal{
@Override
public void eat() {
System.out.println("吃猪草");
}
}
创建切面----
切点有两种方式一种
1.表达式-直接写表达式
2.注解-需要创建注解
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AnimalAnnotationHandler {
/**
* 切点- 表达式
*/
@Pointcut("execution(* com.xx.job.aop.*.*(..))")
public void eatPoint(){}
/**
* 切点- 注解
*/
@Pointcut("@annotation(SysLog)")
public void lookHousePoint(){}
/**
* 后置通知
*/
@After("eatPoint()")
public void startEat(){
System.out.println("吃完了");
}
/**
* 前置通知
*/
@Before("eatPoint()")
public void endEat(){
System.out.println("开始吃");
}
@Before("lookHousePoint()")
public void endLook(){
System.out.println("看家开始");
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value();
}
测试
import com.xx.job.aop.Dog;
import com.xx.job.aop.Pig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class AopApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);
Dog dog = context.getBean(Dog.class);
Pig pig = context.getBean(Pig.class);
dog.eat();
dog.lookHouse();
pig.eat();
}
}
结果
开始吃
吃骨头
吃完了
开始吃
看家开始
看门
吃完了
开始吃
吃猪草
吃完了