一文帶你吃透Spring容器Bean核心模組,面試官再也為難不了你啦

Bean管理

如圖1。2所示為Bean被Spring容器組裝的簡單過程。首先透過XML配置、註解配置或Java配置等3種方式配置元資料,然後裝配BeanDefinition屬性,如果有增強設定,如實現了BeanFactoryPostProcessor或BeanPostProcessor介面,則進行攔截增強處理,最後透過配置的初始化方法完成Bean的例項化。

一文帶你吃透Spring容器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等。

一文帶你吃透Spring容器Bean核心模組,面試官再也為難不了你啦

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的代理模式實現的。

一文帶你吃透Spring容器Bean核心模組,面試官再也為難不了你啦

本次首先介紹代理模式的定義,然後介紹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等 。

一文帶你吃透Spring容器Bean核心模組,面試官再也為難不了你啦

首先我們來看一個基於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 T getProxy() {

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所示,代理類繼承自目標類,每次呼叫代理類的方法時都會被攔截器攔截,然後在攔截器中呼叫真實目標類的方法。

一文帶你吃透Spring容器Bean核心模組,面試官再也為難不了你啦

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中的術語——負責實現切面程式設計的框架

覺得文章不錯的朋友可以轉發此文關注小編;

感謝大家的支援!