一、动态代理
1. 作用:利用插件一样的东西去解决新的需求,切入到原代码,但可以不改变原有的代码和逻辑。
2. 需求:比如需要添加有一个方法的统计时间/输出日志等,如果在方法中添加过于冗余。
3. 缺点:不能对单独的一个方法进行控制。
4. 如何使用:
- 真实对象必须是实现某个接口的对应
- 申明代理对象(接口),在InvocationHandler接口实现,在匿名内部类中去调用真实对象的方法。
- 对代理对象进行实例化,利用Proxy.newProxyInstance,实际是使用了反射机制。
final UserDao userDao = new UserDao();
// 代理对象(快捷方式)
IUserDao iUserDao = null;
//2. 对真实对象的调用逻辑要在InvocationHandler接口里做
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("UserDao " + method.getName() + " run ..");
long start= System.currentTimeMillis();
Object invoke = method.invoke(userDao, args);
long end = System.currentTimeMillis();
System.out.println("UserDao " +method.getName() + " end ..,耗时:" + (end - start) + "毫秒");
//当我们有需要把这些执行数据存到数据库时,我们只需要在这里增加相关的数据库操作代码即可,其它任何地方都不需要做修改。
return invoke;
}
};
// 代理对象实例化:
iUserDao = (IUserDao) Proxy.newProxyInstance(
UserDao.class.getClassLoader(),
new Class[]{IUserDao.class},invocationHandler);
//.getClassLoader() ---->对应的是真实对象
//Class<?>[] ----->真实对象的接口
return iUserDao;
}
二、AOP 面向切面编程
-
定义:OOP的延续,是动态代理的一种实现方式。在运行期间,不修改原代码的情况下对方法进行增强。
-
优势:可以对业务逻辑的各个部分进行隔离,是的业务逻辑的耦合度降低,减少重复代码,提高重用性,提高开发效率
-
理解:AOP实际是对动态代理进行了封装,通过配置的方法来对方法进行增强。
-
重要概念:
- target目标对象: 真实对象
- proxy代理:代理对象(接口)
- joinpoint:拦截点-----> 方法
- poincut切入点:对哪些 Joinpoint 进行拦截 ,相当于查询条件
- advice通知:拦截到joinpoint之后的增强
- aspect切面:把当前类定义为一个切面供容器读取 = poincut + advice
- weaing织入:把advice应用到target的过程,spring采用动态织入
5. 步骤:
a 导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
b 创建target和接口proxy
public class Target implements ITarget{
@Override
public void method() {
System.out.println("run......");
}
}
c 创建aspect切面
public class MyAspect {
public void before(){
System.out.println("before");
}
}
d xml配置
<bean id="target" class="day02.Target"/>
//id为唯一标识,通过反射拿到目标对象
<bean id="aspect" class="day02.MyAspect"/>
//拿到切面
d 导入aop命名空间
xmlns:aop="Index of /schema/aop"
Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
e 配置织入关系
<aop:config>
<aop:aspect ref="aspect">
//调用id为aspect对应的方法
<aop:before method="before" pointcut="execution(public void day02.Target.method())"></aop:before>
//在拦截点前执行方法名为before的方法
//execution就是切入条件(我要在那些点上做增强)
</aop:aspect>
</aop:config>
----------------------------------------------------------------------------------------------------------------------------
**execution([修饰符] 返回值类型 包名.类名.方法名(参数))
----------------------------------------------------------------------------------------------------------------------------
***关于环绕:
通常配合ProceedingJoinPoint
public void around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("around welcome 调用的方法名:" + proceedingJoinPoint.getSignature().getName()); System.out.println("around 调用类:" + proceedingJoinPoint.getSignature().getDeclaringTypeName()); System.out.println("around 调用类名:" + proceedingJoinPoint.getSignature().getDeclaringType().getSimpleName()); try { proceedingJoinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕"); } //输出为: //around welcome 调用的方法名:method //around 调用类:day02.ITarget //around 调用类名:ITarget //run...... //环绕