Bean管理
如圖1。2所示為Bean被Spring容器組裝的簡單過程。首先透過XML配置、註解配置或Java配置等3種方式配置元資料,然後裝配BeanDefinition屬性,如果有增強設定,如實現了BeanFactoryPostProcessor或BeanPostProcessor介面,則進行攔截增強處理,最後透過配置的初始化方法完成Bean的例項化。
圖1。2 Bean的組裝過程
spring-beans模組是Spring容器組裝Bean的核心模組,它提供了組裝Bean的幾個關鍵介面,如圖1。2中的BeanDefinition、BeanFactoryPostProcessor、BeanPost-Processor和BeanFactory等。
BeanDefinition:該介面繼承自AttributeAccessor和BeanDefinition兩個介面。該介面可以獲取Bean的元資料配置資訊,也可以改變Bean的屬性。
BeanFactoryPostProcessor:該介面為函式介面,只有一個方法postProcessBean-Factory()。該介面可以透過ConfigurableListableBeanFactory引數獲取Bean-Definition,然後對Bean的屬性進行修改,如把Bean的Scope從singleton改為prototype等。
BeanPostProcessor:該介面有兩個方法,即postProcessBeforeInitialization()和postProcessAfterInitialization(),分別用於在Bean例項化之前和例項化之後進行額外的操作。BeanPostProcessor介面與BeanFactoryPostProcessor介面的區別在於,BeanFactoryPostProcessor介面是在Bean例項化之前進行修改。
本節將透過兩個簡單的例子,展現BeanFactoryPostProcessor和BeanPostProcessor介面的擴充套件能力。首先來看一個BeanFactoryPostProcessor介面擴充套件的例子。BeanFactory PostProcessor介面方法的輸入引數是ConfigurableListableBeanFactory,使用該引數可以獲取相關Bean的定義資訊。示例程式碼如下:
@Component
public class BeanFactoryPostProcessorImpl implements
BeanFactory
PostProcessor {
@Override
public void
postProcessBeanFactory(ConfigurableListableBean
Factory beanFactory) throws BeansException {
//獲取UserService的BeanDefinition
BeanDefinition beanDefinition =
beanFactory。getBeanDefinition
(“userService”);
//修改Scope屬性
beanDefinition。setScope(“prototype”);
System。out。println(beanDefinition。
getScope());
}
}
列印結果:
prototype
透過列印結果可以看到,在UserService例項化之前修改了該類的作用域,將其從singleton改為了prototype。
對於BeanPostProcessor介面的擴充套件,可以在Spring容器例項化Bean之後或者執行Bean的初始化方法之前新增一些自己的處理邏輯。示例程式碼如下:
@Component
public class BeanPostProcessorImpl implements
BeanPostProcessor {
//在例項化之前操作
@Override
public Object postProcessBeforeInitialization(Object
bean, String
beanName) throws BeansException {
//判斷Bean的型別
if(bean instanceof UserService){
System。out。println(“postProcessBeforeInitialization bean :
” + beanName);
}
return bean;
}
//在例項化之後操作
@Override
public Object postProcessAfterInitialization(Object
bean, String
beanName) throws BeansException {
//判斷Bean的型別
if(bean instanceof UserService){
System。out。println(“postProcessAfterInitialization bean : ”
+ beanName);
}
return bean;
}
}
列印結果:
postProcessBeforeInitialization bean : userService
postProcessAfterInitialization bean : userService
從列印結果中可以看到,在UserService例項化之前和之後都列印了日誌,因此透過BeanPostProcessor可以做一些增強邏輯。
面向切面程式設計
AOP(Aspect Oriented Programming)與OOP(Object Oriented Programming,面向物件程式設計)相輔相成。AOP提供了與OOP不同的抽象軟體結構的視角。在OOP中,我們以類(Class)作為基本單元,而在AOP中則以切面(Aspect)作為基本單元。AOP是一種增強的程式設計方式,可以解耦一些非業務邏輯,如宣告式事務管理、日誌管理或異常處理等。從底層原理來講,AOP實際上是基於Java的代理模式實現的。
本次首先介紹代理模式的定義,然後介紹AOP程式設計概念,最後使用@Aspect註解實現面向切面程式設計。
代理模式
代理模式是經典的設計模式之一,目的是為了擴充套件和增強類或介面。代理模式通常可以分為靜態代理模式和動態代理模式。
1。 靜態代理模式
靜態代理模式的實現比較簡單,主要的實現原理是:代理類與被代理類同時實現一個主題介面,代理類持有被代理類的引用。
(1)新建一個公共介面UserInterface,程式碼如下:
//宣告UserInterface介面
public interface UserInterface {
//宣告方法
public abstract void getUser();
}
(2)定義真實執行類RealUser並實現公共介面UserInterface,程式碼如下:
//宣告RealUser類,實現UserInterface介面
public class RealUser implements UserInterface {
@Override
public void getUser() {
//新建UserService物件
System。out。println(“真實使用者角色執行!”);
UserService userService = new UserService();
userService。setId(1);
userService。setName(“zhangsan”);
userService。getUser();
}
}
(3)定義代理類UserProxy實現公共介面UserInterface,並持有被代理類的例項。在執行時,呼叫被代理類(RealUser)例項的getUser()方法。程式碼如下:
//宣告UserProxy代理類,並實現UserInterface介面
public class UserProxy implements UserInterface {
private UserInterface userInterface;
//構造方法傳入UserInterface型別引數
public UserProxy(UserInterface userInterface) {
this。userInterface = userInterface;
}
//實現getUser()方法,在執行方法前後進行額外操作
@Override
public void getUser() {
doBefore();
userInterface。getUser();
doAfter();
}
//真實方法執行前操作
private void doBefore() {
System。out。println(“代理類開始執行”);
}
//真實方法執行後操作
private void doAfter() {
System。out。println(“代理類結束執行”);
}
}
(4)編寫測試程式碼,具體如下:
public class SpringProxyTest {
public static void main(String[] args) {
UserInterface realUser = new RealUser();
//傳入真實物件RealUser
UserProxy userProxy = new
UserProxy(realUser);
userProxy。getUser();
}
}
執行結果如下:
代理類開始執行
真實使用者角色執行!
id:1
name:zhangsan
代理類結束執行
從列印結果可以看到,代理類實際上是呼叫了被代理類的方法。
2。 動態代理
顧名思義,動態代理是指在程式執行時動態地建立代理類。動態代理的使用方式主要分為兩種:一種是基於介面的代理,另一種則是基於類的代理。基於介面的代理方式是指透過JDK自帶的反射類來生成動態代理類;基於類的代理方式則是指透過位元組碼處理來實現類代理,如CGLIB和Javassist等 。
首先我們來看一個基於JDK反射生成代理類的例子。
(1)定義一個公共介面UserServiceInterface,程式碼如下:
public interface UserServiceInterface {
public void getUser();
}
(2)定義真實使用者角色類UserServiceImpl並實現公共介面 UserServiceInterface,程式碼如下:
public class UserServiceImpl implements UserServiceInterface
{
@Override
public void getUser() {
System。out。println(“zhangsan”); //實現
getUser()方法
}
}
(3)定義代理類UserServiceProxy,實現InvocationHandler介面,並重寫invoke()方法,程式碼如下:
//定義實現InvocationHandler介面的代理類UserServiceProxy
public class UserServiceProxy implements InvocationHandler {
private Object target;
//構造方法
public UserServiceProxy(Object target) {
this。target = target;
}
//透過Proxy動態生成代理類物件
public
return (T)
Proxy。newProxyInstance(target。getClass()。get
ClassLoader(), target。getClass()。getInterfaces(), this);
}
//動態執行方法
@Override
public Object invoke(Object proxy, Method method,
Object[] args)
throws Throwable {
System。out。println(“JDK before”);
method。invoke(target, args);
System。out。println(“JDK after”);
return null;
}
}
(4)編寫測試程式碼:
public class SpringProxyTest {
public static void main(String[] args) {
//透過代理類生成UserServiceInterface介面型別物件
UserServiceInterface userServiceInterface =
new UserService
Proxy(new UserServiceImpl())。getProxy();
userServiceInterface。getUser(); //呼叫
getUser( )方法
}
}
列印結果如下:
JDK proxy before
zhangsan
JDK proxy after
透過上面的代理類的執行結果可以看到,真實使用者角色類被遮蔽了,只需要暴露介面即可執行成功。遮蔽內部實現的邏輯就是代理模式的特點。
上面主要講的是基於JDK反射的例子。下面來看一下CGLIB實現動態代理的原理。它是透過繼承父類的所有公共方法,然後重寫這些方法,並在重寫方法時對這些方法進行增強處理來實現的。根據里氏代換原則(LSP),父類出現的地方子類都可以出現,因此CGLIB實現的代理類也是可以被正常使用的。
CGLIB的基本架構如圖1。3所示,代理類繼承自目標類,每次呼叫代理類的方法時都會被攔截器攔截,然後在攔截器中呼叫真實目標類的方法。
CGLIB實現動態代理的方式比較簡單,具體如下:
(1)直接實現MethodInterceptor攔截器介面,並重寫intercept()方法。程式碼如下:
//繼承MethodInterceptor類並實現intercept()方法
public class UserMethodInterceptor implements
MethodInterceptor {
@Override
public Object intercept(Object obj, Method method,
Object[] args,
MethodProxy proxy) throws Throwable {
System。out。println(“Cglib before”);
proxy。invokeSuper(obj, args);
System。out。println(“Cglib after”);
return null;
}
}
(2)新建Enhancer類,並設定父類和攔截器類。程式碼如下:
public class SpringProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer。setSuperclass(UserServiceImpl。class); //
設定父類
//設定攔截器
enhancer。setCallback(new
UserMethodInterceptor());
UserServiceImpl userServiceImpl =
(UserServiceImpl) enhancer。
create();
//建立物件
userServiceImpl。getUser();
//呼叫getUser方法
}
}
列印結果如下:
Cglib before
zhangsan
Cglib after
JDK實現動態代理是基於介面,其中,目標類與代理類都繼承自同一個介面;而CGLIB實現動態代理是繼承目標類並重寫目標類的方法。在專案開發過程中,可根據實際情況進行選擇。
本文給大家講解的內容是Bean管理;
下篇文章給大家講解的是
AOP中的術語——負責實現切面程式設計的框架
;
覺得文章不錯的朋友可以轉發此文關注小編;
感謝大家的支援!