1. 代理模式
我们以前有学过面向对象,今天所要说的面向切面 。这两个的区别其实不大
面向切面的目的是什么? 增强对象
比如说 我们写了一个对象 或者 一个类 ,我想让这个对象里的方法在之前的功能基础之上在干点其他的事情。
在这里 一定要提一下 开闭原则
对添加放开 对修改关闭 ,如果说:我写了一个类 ,类里有一对方法,我现在想让这个方法进行增强,但是原方法不变,这咋办呢?
我这里有一个class A :
class A{
a();
}
在这个方法基础之上进行增强 我们可以搞一个 B 去继承 A ,重写A的方法
那么 我们进行一个小总结
面向对象 进行 增强 - - 》 继承方式 和 代理(设计模式)
设计模式 - - 》 单例 :这个对象只能被new一次,这个对象在当前JVM中有且仅有一个实例叫做单例
工厂 :屏蔽某个通向生产的细节,直接从工厂里面拿到就行了
模板方法(IO) :在IO流里面去继承的时候继承的都是类而不是实现的方法
策略模式(if-else switch) 改造的是if-else 和 switch
代理模式 如何实现面向对象的增强呢?我们来看一下代理的几个概念
代理对象: 对代理对象被代理之后生成的对象
目标对象(被代理对象):
增强:目标对象被增强的部分就是增强
举个小例子:
StuService.java
//使用代理的方式 开闭原则 此代码不能修改(被代理对象) 目标对象
public class StuService {
public void del(){
System.out.println("StuService·····直接删除·····");
}
}
StuServiceProxy.java
//代理对象要持有一个代理对象的实例 代理对象
public class StuServiceProxy {
private StuService target;
//限制构造方法 来构造的时候必须给我一个被代理对象
public StuServiceProxy(StuService stuService){
this.target=stuService;
}
//调方法的时候调用我这个方法
public void proxyDel(){
System.out.println("权限校验"); 增强
target.del();
}
}
但是我们要手动的要对每一个目标对象生成一个代理对象 这就是静态代理 可扩展性差
所以说 我们就有了动态代理 :给我一个对象 我就生成一个他的代理对象
而在动态代理里分为两种形式 :JDK动态代理(面向接口) 和 cglib动态代理(asm:手动更改字节码的框架)
1.1 动态代理
必须要有一个接口:
public interface StuServiceI {
public void del();
}
接口实现类
//(被代理对象)
public class StuService implements StuServiceI {
public void del(){
System.out.println("StuService·····直接删除·····");
}
}
JdkProxy
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* jsk动态代理类的实现
* */
public class JdkProxy {
//声明目标对象
private Object target;
//我要代理那个类 你只要把实例给我一个就可以了
public JdkProxy(Object target){
this.target=target;
}
//通过newProxyInstance 生成代理对象
public Object getProxy(){
//1.类加载器 (双亲委派)
//2.目标对象的接口 ()
//3.InvocationHandler
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
// 1. proxy :代理类
// 2. method : 目标对象中的方法
// 3. arg:目标对象中要执行方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法校验");
//调用目标对象中的方法
//参数 :当前在那个对象上执行 ,目标对象中要执行方法的参数
Object result = method.invoke(target, args);
return result;
}
});
}
public static void main(String[] args) {
StuService stuService = new StuService();
//强转成他的接口类型
StuServiceI jdkProxy = (StuServiceI)new JdkProxy(stuService).getProxy();
jdkProxy.del();
}
}
1.2 cglib动态代理
加入目标没有接口 那我们就使用cglib动态代理 :你给我一个目标对象 我会给你的目标对象生成一个子类
//目标对象
//使用代理的方式 开闭原则 此代码不能修改(被代理对象)
public class StuService {
public void del(){
System.out.println("StuService·····直接删除·····");
}
}
//代理对象
package proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy {
//声明目标对象
private Enhancer enhancer=new Enhancer();
public Object getProxy(Class clazz){
//设置父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MethodInterceptor() {
//1 .o
//2 .method 目标类要执行的方法
//3 .objects 目标类要执行的方法参数
//4 .代理的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
});
return enhancer.create();
}
public static void main(String[] args) {
StuService proxy = (StuService) new CglibProxy().getProxy(StuService.class);
proxy.del();
}
}
1.3 两种代理的总结
2. AOP面向切面
2.1 什么是AOP
AOP Aspect Oriented Programing 面向切面编程
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java 语言,提供了一个专门的编译器,在编译时提供横向代码的织入
2.2 AOP相关术语
Joinpoint(连接点): 我要对某个类名的某个方法进行增强 ,所有的方法就是连接点
Pointcut(切入点) :在这个基础之上 缩小一层 ,比如 一个类名里面有10个方法 这10个方法都可以称之为连接点,但是 我只对里面的3个方法进行增强 ,这3个方法就是切入点
Advice(通知/增强) :切入点增强的功能
Introduction(引介): 引介是一种特殊的通知,在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象): 代理的目标对象
**Weaving(织入): ** 是指把增强应用到目标对象来创建新的代理对象的过程.
spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
**Proxy(代理): ** 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面) : 是切入点和通知(引介)的结合
2.3 AOP增强类型
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性
2.4 使用AspectJ实现AOP
通过配置启用@AspectJ切面
<aop:aspectj-autoproxy />
<context:component-scan base-package="aop"></context:component-scan>
代码演示
定义一个接口
public interface PersonService {
int add(String name);
void del(int id);
}
接口实现类
package aop;
import org.springframework.stereotype.Component;
@Component
public class PersonServiceI implements PersonService {
@Override
public int add(String name) {
System.out.println("add·····"+name);
return 250;
}
@Override
public void del(int id) {
System.out.println("del"+id);
}
}
切面类:
package aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/*
* 切面类 : 增强+切入点
*
* */
@Component
@Aspect
public class MyAspectJ {
// 定义切入点信息 execution表达式
@Before(value = "execution(* aop.PersonServiceI.*(..))")
public void myBefore(){
System.out.println("前置通知");
}
// 获取目标方法的返回值 returning :拿到的返回值的名称
@AfterReturning(value = "execution(* aop.PersonServiceI.add(..))",returning = "re")
public void myBeforeAfterR(Object re){
System.out.println("前置通知");
System.out.println("返回值是"+re);
}
// 环绕通知 可以控制目标方向的运行
@Around(value = "execution(* aop.PersonServiceI.add(..))")
public Object myBeforeAround(ProceedingJoinPoint joinPoint){
System.out.println("环绕通知前");
try {
Object o = joinPoint.proceed();
System.out.println("返回值是"+o);
System.out.println("环绕通知后");
return o;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
//异常通知: 只有目标异常 抛出异常 才会执行
@AfterThrowing(value = "execution(* aop.PersonServiceI.add(..))",throwing = "ex")
public void myAfterThrowing(JoinPoint joinPoint,Throwable ex){
System.out.println("异常通知");
System.out.println("ex.getMessage() = " + ex.getMessage());
}
//最终通知 :不管有无异常 最终都会通知
@After(value = "execution(* aop.PersonServiceI.add(..))")
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知 ");
}
}
测试类:
public class TestAop {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("bean.xml");
PersonService personService = applicationContext.getBean(PersonService.class);
personService.add("admin");
}
}