目录
1.为什么要使用代代理模式
出现的问题:
对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
附加功能分散在各个业务功能方法中,不利于统一维护
解决思路:解耦。我们需要把附加功能从业务功能代码中抽取出来。
传统的oop模式:只能对一段连续的代码进行封装,抽取,然后再进行使用
aop:抽取不连续的,也称切面工程
2.静态代理
public class CalculatorStaticProxy implements Calculator {
@Override
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
// 通过目标对象来实现核心业务逻辑
int addResult = target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:" + addResult);
return addResult;
}
}
虽然可以实现了解耦,但是重复的代码依旧很多,又能还更加的麻烦了,不具备灵活性
3.动态代理
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* 1、classLoader:加载动态生成的代理类的类加载器
* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接
口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method所对应方法的参数
*/
Object result = null;
try { //执行之前,可以进行操作
result = method.invoke(target, args); //通过反射,来获取方法
} catch (Exception e) { //抛异常,可以进行操作
e.printStackTrace();
} finally { //结束可以继续操作
}
return result;
}
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
};
}
测试一下
@Test
public void testDynamicProxy(){
//直接传入响应的功能是实现类即可
ProxyFactory factory = new ProxyFactory(new CalculatorLogImpl());
Calculator proxy = (Calculator) factory.getProxy();
proxy.div(1,0);
}
4.AOP的了解与使用
1.AOP介绍
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面 (oop)向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况 下给程序动态统一添加额外功能的一种技术。
2.AOP相关术语
1.横切关注点
2.通知
3.切面
4.目标
5.代理
6.连接点
3.AOP的使用
1.注解方法
四种方法,获取不同的处理时机,
前置通知:使用@Before注解标识,在被代理的目标方法前执行
返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝)
异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命)
后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕整个被代理的目标方法,包 括上面四种通知对应的所有位置
也可以利用pointcut,直接把注解的位置给抽取出来
在这些方法中还有一些另外的参数
比如在循环around中会有一个result
@Aspect //表示这个类是一个切面类
@Component //注解
public class LogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))") //这是实现类的全路径,
标准规则为:@注解("execution(* 全路径)") *标识全部
public void beforeMethod(JoinPoint joinPoint){
//获取方法名
String methodName = joinPoint.getSignature().getName();
//获取方法参数
String args = Arrays.toString(joinPoint.getArgs());
//如果获取的防暑是object类型的,需要用Arrarys.toString()进行转换
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
xml中配置环境:
//扫描组件
<context:component-scan base-package="com.atguigu.aop.annotation">
</context:component-scan>
//开启注解扫描
<aop:aspectj-autoproxy />
一些特殊的属性
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*
(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result)
//可以获取到result然后再利用try-catech-finial,可以在输出前后进行抛异常和最后,进行输入语句
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标方法的执行,目标方法的返回值一定要返回给外界调用者
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
切面优先级,如果都是对统一实现类来进行继承的话,那就会有竞争的趋势
所我们就会设置切面优先级
@Order(较小的数):优先级高
2.xml的方法
<context:component-scan base-package="com.atguigu.aop.xml"></context:component-scan>
<aop:config>
<!--配置切面类-->
<aop:aspect ref="loggerAspect">
<aop:pointcut id="pointCut"expression="execution(*com.atguigu.aop.xml.CalculatorImpl.*(..))"/>//切面标识
<aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before>
<aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after>
<aop:after-returning method="afterReturningMethod" returning="result"
pointcut-ref="pointCut"></aop:after-returning>
<aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcutref="pointCut"> </aop:after-throwing>
<aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around>
</aop:aspect>
//设置优先级
<aop:aspect ref="validateAspect" order="1">
<aop:before method="validateBeforeMethod" pointcut-ref="pointCut">
</aop:before>
</aop:aspect>
</aop:config>
总结
- 切面编程,首先要了解静态代理和动态代理
- 切面编程,是为了解决OOP不能抽取方法的不连续代码的问题
- 切面编程,一般采用注解+xml的方式
- xml的方式就是扫描组件以及开启扫描<aop:aspectj-autoproxy />
- 注解的方式就是应用了应用了@Aspect和@component还有@after还有joinpoint,order属性等等