1.AOP
AOP(面向切面)是一種編程范式蝠引,提供從另一個角度來考慮程序結(jié)構(gòu)以完善面向?qū)ο缶幊蹋∣OP)。
AOP為開發(fā)者提供了一種描述橫切關注點的機制董栽,并能夠自動將橫切關注點織入到面向?qū)ο蟮能浖到y(tǒng)中,從而實現(xiàn)了橫切關注點的模塊化。
AOP能夠?qū)⒛切┡c業(yè)務無關杉畜,卻為業(yè)務模塊所共同調(diào)用的邏輯或責任,例如事務處理假栓、日志管理寻行、權(quán)限控制等,封裝起來匾荆,便于減少系統(tǒng)的重復代碼拌蜘,降低模塊間的耦合度,并有利于未來的可操作性和可維護性牙丽。
使用AOP的好處
- 降低模塊的耦合度
- 使系統(tǒng)容易擴展
- 提高代碼復用性
AOP的基本概念
- 連接點(JoinPoint):需要在程序中插入橫切關注點的點简卧,連接點可能是在類初始化、方法調(diào)用烤芦、字段調(diào)用或處理異常等等举娩。Spring中只支持方法執(zhí)行連接點。
- 切入點(Pointcut):一組相關連接點的集合构罗。
- 通知(Advice):在連接點上執(zhí)行的行為铜涉,增強提供了在AOP中需要在切入點所選擇的連接點處進行擴展現(xiàn)有行為的手段。包括前置增強(before advice)遂唧、后置增強 (after advice)芙代、環(huán)繞增強 (around advice)。
- 切面(Aspect):通知和切入點的結(jié)合盖彭。
- 織入(Weaving):織入是一個過程纹烹,是將切面應用到目標對象從而創(chuàng)建出AOP代理對象的過程。
- 代理(Proxy):通過代理方式來對目標對象應用切面召边。AOP代理可以用JDK動態(tài)代理或CGLIB代理實現(xiàn)铺呵。
-
目標對象(Target):需要被織入關注點的對象。即被代理的對象隧熙。
實現(xiàn)AOP的主要設計模式就是動態(tài)代理片挂。
Spring的動態(tài)代理有兩種:一是JDK的動態(tài)代理;另一個是cglib動態(tài)代理贱鼻。
JDK動態(tài)代理模擬
JDK動態(tài)代理的兩個核心接口(類)分別是InvocationHandler和Proxy宴卖。注意:只能代理接口。
public class TimeHandler implements InvocationHandler {
// 目標對象
private Object targetObject;
public TimeHandler(Object targetObject){
this.targetObject = targetObject;
}
@Override
//關聯(lián)的這個實現(xiàn)類的方法被調(diào)用時將被執(zhí)行
/*InvocationHandler接口的方法邻悬,proxy表示代理症昏,method表示原對象被調(diào)用的方法,
args表示方法的參數(shù)*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object ret=null;
try{
System.out.println("方法之前:"+System.currentTimeMillis());
//調(diào)用目標方法
ret=method.invoke(targetObject, args);
System.out.println("方法之后:"+System.currentTimeMillis());
}catch(Exception e){
e.printStackTrace();
System.out.println("error");
throw e;
}
return ret;
}
}
TimeHandler 類實現(xiàn)了InvocationHandler接口父丰。實現(xiàn)核心方法invoke肝谭,共有3個參數(shù)掘宪。第一個參數(shù) 生成的代理類實例,第二個參數(shù) 目標對象的方法攘烛,第三個參數(shù) 方法的參數(shù)值數(shù)組魏滚。
public class ProxyUtil {
@SuppressWarnings("unchecked")
public static <T> T proxyOne(ClassLoader loader,Class<?>[] clz,InvocationHandler handler){
return (T)Proxy.newProxyInstance(loader, clz, handler);
}
}
ProxyUtil 類簡單封裝了一下Proxy.newProxyInstance()方法。該方法也有3個參數(shù)坟漱。第一個參數(shù)產(chǎn)生代理對象的類加載器鼠次,第二個參數(shù)目標對象的接口數(shù)組,第三個參數(shù)就是實現(xiàn)InvocationHandler接口的類實例芋齿。
public interface UserManager {
public void addUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("addUser(id:"+userId+",name:"+userName+")");
}
}
public static void main(String[] args) {
UserManager um=new UserManagerImpl();
LogHandler log =new LogHandler(um);
um=ProxyUtil.proxyOne(um.getClass().getClassLoader(),
um.getClass().getInterfaces(), log);
TimeHandler time = new TimeHandler(um);
um=ProxyUtil.proxyOne(um.getClass().getClassLoader(),
um.getClass().getInterfaces(), time);
um.addUser("1111", "張三");
}
為了演示需要腥寇,這邊又增加了一個LogHandler,跟TimeHandler代碼一樣觅捆。
CGLIB動態(tài)代理模擬
CGLIB動態(tài)代理的兩個核心接口(類)分別是MethodInterceptor和Enhancer赦役。是不是跟JDK動態(tài)代理很相似,用法也差不多栅炒。但CGLIB可以代理類和接口掂摔。注意:不能代理final類。
public class TimeInterceptor implements MethodInterceptor {
private Object target;
public TimeInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object proxy, Method method,
Object[] args, MethodProxy invocation) throws Throwable {
System.out.println("方法之前:"+System.currentTimeMillis());
Object ret = invocation.invoke(target, args);
System.out.println("方法之后:"+System.currentTimeMillis());
return ret;
}
}
intercept方法4個參數(shù)赢赊。1.生成的代理類實例乙漓。2.被代理對象的方法引用。3.方法參數(shù)值數(shù)組释移。4.代理類對方法的代理引用簇秒。
public class ProxyUtil {
@SuppressWarnings("unchecked")
public static <T> T proxyOne(Class<?> clz,MethodInterceptor interceptor){
return (T)Enhancer.create(clz, interceptor);
}
}
Enhancer類是CGLib中的字節(jié)碼增強器。
public class UserManage {
public void addUser(String userId, String userName) {
System.out.println("addUser(id:"+userId+",name:"+userName+")");
}
}
public static void main(String[] args) {
UserManage um = new UserManage();
TimeInterceptor time = new TimeInterceptor(um);
um = ProxyUtil.proxyOne(um.getClass(), time);
um.addUser("111", "老王");
}
2.IOC
IOC(控制反轉(zhuǎn))就是依賴倒置原則的一種代碼設計思路秀鞭。就是把原先在代碼里面需要實現(xiàn)的對象創(chuàng)建、對象之間的依賴扛禽,反轉(zhuǎn)給容器來幫忙實現(xiàn)锋边。
Spring IOC容器通過xml,注解等其它方式配置類及類之間的依賴關系,完成了對象的創(chuàng)建和依賴的管理注入编曼。實現(xiàn)IOC的主要設計模式是工廠模式豆巨。
使用IOC的好處
- 集中管理,實現(xiàn)類的可配置和易管理掐场。
- 降低了類與類之間的耦合度往扔。
簡單模擬IOC
public interface BeanFactory {
Object getBean(String id);
}
public class ClassPathXmlApplicationContext implements BeanFactory {
//容器,用來存放注入的Bean
private Map<String, Object> container = new HashMap<String, Object>();
//解析xml文件熊户,通過反射將配置的bean放到container中
public ClassPathXmlApplicationContext(String fileName) throws Exception{
SAXBuilder sb = new SAXBuilder();
Document doc
=sb.build(ClassPathXmlApplicationContext.class.getResource("/"+fileName));
Element root = doc.getRootElement();
List<Element> list = XPath.selectNodes(root, "/beans/bean");
for (int i = 0; i < list.size(); i++) {
Element bean = list.get(i);
String id = bean.getAttributeValue("id");
String clazz = bean.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
container.put(id, o);
}
}
@Override
public Object getBean(String id) {
return container.get(id);
}
}
需要導入 jdom.jar包萍膛。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="people" class="com.ioc.People" />
<bean id="chicken" class="com.ioc.Chicken" />
<bean id="dog" class="com.ioc.Dog" />
</beans>
public interface Animal {
void say();
}
public class Dog implements Animal {
@Override
public void say() {
System.out.println("汪汪");
}
}
public class Chicken implements Animal {
@Override
public void say() {
System.out.println("雞你很美");
}
}
public class People {
public void info(){
System.out.println("小明-23歲");
}
}
public static void main(String[] args) throws Exception {
//加載配置文件
BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
Object os = f.getBean("dog");
Animal dog = (Animal)os;
dog.say();
Object op = f.getBean("chicken");
Animal chicken = (Animal)op;
chicken.say();
Object p = f.getBean("people");
People people= (Animal)p;
people.info();
}
參考資料
我對AOP的理解
源碼解讀Spring IOC原理