spring(二、bean生命周期、用到的設計模式、常用注解)
Spring作為當前Java最流行、最強大的輕量級框架,受到了程序員的熱烈歡迎。准確的了解Spring Bean的生命周期是非常必要的。我們通常使用ApplicationContext作為Spring容器。這里,我們講的也是 ApplicationContext中Bean的生命周期。而實際上BeanFactory也是差不多的,只不過處理器需要手動注冊。
轉載 http://www.cnblogs.com/zrtqsk/p/3735273.html
一、生命周期流程圖:
Spring Bean的完整生命周期從創建Spring容器開始,直到最終Spring容器銷毀Bean,這其中包含了一系列關鍵點。
若容器注冊了以上各種接口,程序那么將會按照以上的流程進行。下面將仔細講解各接口作用。
二、各種接口方法分類
Bean的完整生命周期經歷了各種方法調用,這些方法可以划分為以下幾類:
1、Bean自身的方法 : 這個包括了Bean本身調用的方法和通過配置文件中<bean>的init-method和destroy-method指定的方法
2、Bean級生命周期接口方法 : 這個包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這些接口的方法
3、容器級生命周期接口方法 : 這個包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個接口實現,一般稱它們的實現類為“后處理器”。
4、工廠后處理器接口方法 : 這個包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工廠后處理器 接口的方法。工廠后處理器也是容器級的。在應用上下文裝配配置文件之后立即調用。
三、演示
我們用一個簡單的Spring Bean來演示一下Spring Bean的生命周期。
1、首先是一個簡單的Spring Bean,調用Bean自身的方法和Bean級生命周期接口方法,為了方便演示,它實現了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這4個接口,同時有2個方法,對應配置文件中<bean>的init-method和destroy-method。如下:
1 package springBeanTest; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.factory.BeanFactory; 5 import org.springframework.beans.factory.BeanFactoryAware; 6 import org.springframework.beans.factory.BeanNameAware; 7 import org.springframework.beans.factory.DisposableBean; 8 import org.springframework.beans.factory.InitializingBean; 9 10 /** 11 * @author qsk 12 */ 13 public class Person implements BeanFactoryAware, BeanNameAware, 14 InitializingBean, DisposableBean { 15 16 private String name; 17 private String address; 18 private int phone; 19 20 private BeanFactory beanFactory; 21 private String beanName; 22 23 public Person() { 24 System.out.println("【構造器】調用Person的構造器實例化"); 25 } 26 27 public String getName() { 28 return name; 29 } 30 31 public void setName(String name) { 32 System.out.println("【注入屬性】注入屬性name"); 33 this.name = name; 34 } 35 36 public String getAddress() { 37 return address; 38 } 39 40 public void setAddress(String address) { 41 System.out.println("【注入屬性】注入屬性address"); 42 this.address = address; 43 } 44 45 public int getPhone() { 46 return phone; 47 } 48 49 public void setPhone(int phone) { 50 System.out.println("【注入屬性】注入屬性phone"); 51 this.phone = phone; 52 } 53 54 @Override 55 public String toString() { 56 return "Person [address=" + address + ", name=" + name + ", phone=" 57 + phone + "]"; 58 } 59 60 // 這是BeanFactoryAware接口方法 61 @Override 62 public void setBeanFactory(BeanFactory arg0) throws BeansException { 63 System.out 64 .println("【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory()"); 65 this.beanFactory = arg0; 66 } 67 68 // 這是BeanNameAware接口方法 69 @Override 70 public void setBeanName(String arg0) { 71 System.out.println("【BeanNameAware接口】調用BeanNameAware.setBeanName()"); 72 this.beanName = arg0; 73 } 74 75 // 這是InitializingBean接口方法 76 @Override 77 public void afterPropertiesSet() throws Exception { 78 System.out 79 .println("【InitializingBean接口】調用InitializingBean.afterPropertiesSet()"); 80 } 81 82 // 這是DiposibleBean接口方法 83 @Override 84 public void destroy() throws Exception { 85 System.out.println("【DiposibleBean接口】調用DiposibleBean.destory()"); 86 } 87 88 // 通過<bean>的init-method屬性指定的初始化方法 89 public void myInit() { 90 System.out.println("【init-method】調用<bean>的init-method屬性指定的初始化方法"); 91 } 92 93 // 通過<bean>的destroy-method屬性指定的初始化方法 94 public void myDestory() { 95 System.out.println("【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法"); 96 } 97 }
2、接下來是演示BeanPostProcessor接口的方法,如下:
1 package springBeanTest; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.factory.config.BeanPostProcessor; 5 6 public class MyBeanPostProcessor implements BeanPostProcessor { 7 8 public MyBeanPostProcessor() { 9 super(); 10 System.out.println("這是BeanPostProcessor實現類構造器!!"); 11 // TODO Auto-generated constructor stub 12 } 13 14 @Override 15 public Object postProcessAfterInitialization(Object arg0, String arg1) 16 throws BeansException { 17 System.out 18 .println("BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!"); 19 return arg0; 20 } 21 22 @Override 23 public Object postProcessBeforeInitialization(Object arg0, String arg1) 24 throws BeansException { 25 System.out 26 .println("BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!"); 27 return arg0; 28 } 29 }
如上,BeanPostProcessor接口包括2個方法postProcessAfterInitialization和postProcessBeforeInitialization,這兩個方法的第一個參數都是要處理的Bean對象,第二個參數都是Bean的name。返回值也都是要處理的Bean對象。這里要注意。
3、InstantiationAwareBeanPostProcessor 接口本質是BeanPostProcessor的子接口,一般我們繼承Spring為其提供的適配器類InstantiationAwareBeanPostProcessor Adapter來使用它,如下:
1 package springBeanTest; 2 3 import java.beans.PropertyDescriptor; 4 5 import org.springframework.beans.BeansException; 6 import org.springframework.beans.PropertyValues; 7 import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; 8 9 public class MyInstantiationAwareBeanPostProcessor extends 10 InstantiationAwareBeanPostProcessorAdapter { 11 12 public MyInstantiationAwareBeanPostProcessor() { 13 super(); 14 System.out 15 .println("這是InstantiationAwareBeanPostProcessorAdapter實現類構造器!!"); 16 } 17 18 // 接口方法、實例化Bean之前調用 19 @Override 20 public Object postProcessBeforeInstantiation(Class beanClass, 21 String beanName) throws BeansException { 22 System.out 23 .println("InstantiationAwareBeanPostProcessor調用postProcessBeforeInstantiation方法"); 24 return null; 25 } 26 27 // 接口方法、實例化Bean之后調用 28 @Override 29 public Object postProcessAfterInitialization(Object bean, String beanName) 30 throws BeansException { 31 System.out 32 .println("InstantiationAwareBeanPostProcessor調用postProcessAfterInitialization方法"); 33 return bean; 34 } 35 36 // 接口方法、設置某個屬性時調用 37 @Override 38 public PropertyValues postProcessPropertyValues(PropertyValues pvs, 39 PropertyDescriptor[] pds, Object bean, String beanName) 40 throws BeansException { 41 System.out 42 .println("InstantiationAwareBeanPostProcessor調用postProcessPropertyValues方法"); 43 return pvs; 44 } 45 }
這個有3個方法,其中第二個方法postProcessAfterInitialization就是重寫了BeanPostProcessor的方法。第三個方法postProcessPropertyValues用來操作屬性,返回值也應該是PropertyValues對象。
4、演示工廠后處理器接口方法,如下:
1 package springBeanTest; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.factory.config.BeanDefinition; 5 import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 6 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 7 8 public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 9 10 public MyBeanFactoryPostProcessor() { 11 super(); 12 System.out.println("這是BeanFactoryPostProcessor實現類構造器!!"); 13 } 14 15 @Override 16 public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) 17 throws BeansException { 18 System.out 19 .println("BeanFactoryPostProcessor調用postProcessBeanFactory方法"); 20 BeanDefinition bd = arg0.getBeanDefinition("person"); 21 bd.getPropertyValues().addPropertyValue("phone", "110"); 22 } 23 24 }
5、配置文件如下beans.xml,很簡單,使用ApplicationContext,處理器不用手動注冊:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="beanPostProcessor" class="springBeanTest.MyBeanPostProcessor"> </bean> <bean id="instantiationAwareBeanPostProcessor" class="springBeanTest.MyInstantiationAwareBeanPostProcessor"> </bean> <bean id="beanFactoryPostProcessor" class="springBeanTest.MyBeanFactoryPostProcessor"> </bean> <bean id="person" class="springBeanTest.Person" init-method="myInit" destroy-method="myDestory" scope="singleton" p:name="張三" p:address="廣州" p:phone="15900000000" /> </beans>
6、下面測試一下:
1 package springBeanTest; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class BeanLifeCycle { 7 8 public static void main(String[] args) { 9 10 System.out.println("現在開始初始化容器"); 11 12 ApplicationContext factory = new ClassPathXmlApplicationContext("springBeanTest/beans.xml"); 13 System.out.println("容器初始化成功"); 14 //得到Preson,並使用 15 Person person = factory.getBean("person",Person.class); 16 System.out.println(person); 17 18 System.out.println("現在開始關閉容器!"); 19 ((ClassPathXmlApplicationContext)factory).registerShutdownHook(); 20 } 21 }
關閉容器使用的是實際是AbstractApplicationContext的鈎子方法。
我們來看一下結果:
現在開始初始化容器 2014-5-18 15:46:20 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@19a0c7c: startup date [Sun May 18 15:46:20 CST 2014]; root of context hierarchy 2014-5-18 15:46:20 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [springBeanTest/beans.xml] 這是BeanFactoryPostProcessor實現類構造器!! BeanFactoryPostProcessor調用postProcessBeanFactory方法 這是BeanPostProcessor實現類構造器!! 這是InstantiationAwareBeanPostProcessorAdapter實現類構造器!! 2014-5-18 15:46:20 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@9934d4: defining beans [beanPostProcessor,instantiationAwareBeanPostProcessor,beanFactoryPostProcessor,person]; root of factory hierarchy InstantiationAwareBeanPostProcessor調用postProcessBeforeInstantiation方法 【構造器】調用Person的構造器實例化 InstantiationAwareBeanPostProcessor調用postProcessPropertyValues方法 【注入屬性】注入屬性address 【注入屬性】注入屬性name 【注入屬性】注入屬性phone 【BeanNameAware接口】調用BeanNameAware.setBeanName() 【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory() BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改! 【InitializingBean接口】調用InitializingBean.afterPropertiesSet() 【init-method】調用<bean>的init-method屬性指定的初始化方法 BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改! InstantiationAwareBeanPostProcessor調用postProcessAfterInitialization方法 容器初始化成功 Person [address=廣州, name=張三, phone=110] 現在開始關閉容器! 【DiposibleBean接口】調用DiposibleBean.destory() 【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法
spring中常用的設計模式達到九種,我們舉例說明:
第一種:簡單工廠
又叫做靜態工廠方法(StaticFactory Method)模式,但不屬於23種GOF設計模式之一。
簡單工廠模式的實質是由一個工廠類根據傳入的參數,動態決定應該創建哪一個產品類。
spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得bean對象,但是否是在傳入參數后創建還是傳入參數前創建這個要根據具體情況來定。如下配置,就是在 HelloItxxz 類中創建一個 itxxzBean。
第二種:工廠方法(Factory Method)
//調用getBean()時,返回隨機數.如果沒有指定factory-method,會返回StaticFactoryBean的實例,即返回工廠Bean的實例 XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml")); System.out.println("我是IT學習者創建的實例:"+factory.getBean("random").toString());
第三種:單例模式(Singleton)
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
spring中的單例模式完成了后半句話,即提供了全局的訪問點BeanFactory。但沒有從構造器級別去控制單例,這是因為spring管理的是是任意的java對象。
核心提示點:Spring下默認的bean均為singleton,可以通過singleton=“true|false” 或者 scope=“?”來指定
第四種:適配器(Adapter)
在Spring的Aop中,使用的Advice(通知)來增強被代理類的功能。Spring實現這一AOP功能的原理就使用代理模式(1、JDK動態代理。2、CGLib字節碼生成技術代理。)對類進行方法級別的切面增強,即,生成被代理類的代理類, 並在代理類的方法前,設置攔截器,通過執行攔截器重的內容增強了代理方法的功能,實現的面向切面編程。
第五種:包裝器(Decorator)
在我們的項目中遇到這樣一個問題:我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。我們以往在spring和hibernate框架中總是配置一個數據源,因而sessionFactory的dataSource屬性總是指向這個數據源並且恆定不變,所有DAO在使用sessionFactory的時候都是通過這個數據源訪問數據庫。但是現在,由於項目的需要,我們的DAO在訪問sessionFactory的時候都不得不在多個數據源中不斷切換,問題就出現了:如何讓sessionFactory在執行數據持久化的時候,根據客戶的需求能夠動態切換不同的數據源?我們能不能在spring的框架下通過少量修改得到解決?是否有什么設計模式可以利用呢?
首先想到在spring的applicationContext中配置所有的dataSource。這些dataSource可能是各種不同類型的,比如不同的數據庫:Oracle、SQL Server、MySQL等,也可能是不同的數據源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根據客戶的每次請求,將dataSource屬性設置成不同的數據源,以到達切換數據源的目的。
spring中用到的包裝器模式在類名上有兩種表現:一種是類名中含有Wrapper,另一種是類名中含有Decorator。基本上都是動態地給一個對象添加一些額外的職責。
第六種:代理(Proxy)
為其他對象提供一種代理以控制對這個對象的訪問。 從結構上來看和Decorator模式類似,但Proxy是控制,更像是一種對功能的限制,而Decorator是增加職責。
spring的Proxy模式在aop中有體現,比如JdkDynamicAopProxy和Cglib2AopProxy。
第七種:觀察者(Observer)
定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
spring中Observer模式常用的地方是listener的實現。如ApplicationListener。
第八種:策略(Strategy)
定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
spring中在實例化對象的時候用到Strategy模式
在SimpleInstantiationStrategy中有如下代碼說明了策略模式的使用情況:
第九種:模板方法(Template Method)
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
Template Method模式一般是需要繼承的。這里想要探討另一種對Template Method的理解。spring中的JdbcTemplate,在用這個類時並不想去繼承這個類,因為這個類的方法太多,但是我們還是想用到JdbcTemplate已有的穩定的、公用的數據庫連接,那么我們怎么辦呢?我們可以把變化的東西抽出來作為一個參數傳入JdbcTemplate的方法中。但是變化的東西是一段代碼,而且這段代碼會用到JdbcTemplate中的變量。怎么辦?那我們就用回調對象吧。在這個回調對象中定義一個操縱JdbcTemplate中變量的方法,我們去實現這個方法,就把變化的東西集中到這里了。然后我們再傳入這個回調對象到JdbcTemplate,從而完成了調用。這可能是Template Method不需要繼承的另一種實現方式吧。
Spring的一個核心功能是IOC,就是將Bean初始化加載到容器中,Bean是如何加載到容器的,可以使用Spring注解方式或者Spring XML配置方式。
Spring注解方式減少了配置文件內容,更加便於管理,並且使用注解可以大大提高了開發效率!
下面按照分類講解Spring中常用的一些注解。
一: 組件類注解
思考:Spring怎么知道應該把哪些Java類當成bean注冊到容器中呢?
答案:使用配置文件或者注解的方式進行標識需要處理的java類!
- 1
- 2
1、注解類介紹
@Component :標准一個普通的spring Bean類。
@Repository:標注一個DAO組件類。
@Service:標注一個業務邏輯組件類。
@Controller:標注一個控制器組件類。
這些都是注解在平時的開發過程中出鏡率極高,@Component、@Repository、@Service、@Controller實質上屬於同一類注解,用法相同,功能相同,區別在於標識組件的類型。@Component可以代替@Repository、@Service、@Controller,因為這三個注解是被@Component標注的。如下代碼
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2、舉例詳解
(1)當一個組件代表數據訪問層(DAO)的時候,我們使用@Repository進行注解,如下
@Repository
public class HappyDaoImpl implements HappyDao{
private final static Logger LOGGER = LoggerFactory.getLogger(HappyDaoImpl .class);
public void club(){
//do something ,like drinking and singing
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
(2)當一個組件代表業務層時,我們使用@Service進行注解,如下
@Service(value="goodClubService")
//使用@Service注解不加value ,默認名稱是clubService
public class ClubServiceImpl implements ClubService {
@Autowired
private ClubDao clubDao;
public void doHappy(){
//do some Happy
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
(3)當一個組件作為前端交互的控制層,使用@Controller進行注解,如下
@Controller
public class HappyController {
@Autowired //下面進行講解
private ClubService clubService;
// Control the people entering the Club
// do something
}
/*Controller相關的注解下面進行詳細講解,這里簡單引入@Controller*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3、總結注意點
1、被注解的java類當做Bean實例,Bean實例的名稱默認是Bean類的首字母小寫,其他部分不變。@Service也可以自定義Bean名稱,但是必須是唯一的!
2、盡量使用對應組件注解的類替換@Component注解,在spring未來的版本中,@Controller,@Service,@Repository會攜帶更多語義。並且便於開發和維護!
3、指定了某些類可作為Spring Bean類使用后,最好還需要讓spring搜索指定路徑,在Spring配置文件加入如下配置:
<!-- 自動掃描指定包及其子包下的所有Bean類 -->
<context:component-scan base-package="org.springframework.*"/>
- 1
- 2
二:裝配bean時常用的注解
1、注解介紹
@Autowired:屬於Spring 的org.springframework.beans.factory.annotation包下,可用於為類的屬性、構造器、方法進行注值
@Resource:不屬於spring的注解,而是來自於JSR-250位於java.annotation包下,使用該annotation為目標bean指定協作者Bean。
@PostConstruct 和 @PreDestroy 方法 實現初始化和銷毀bean之前進行的操作
2、舉例說明
(1):@Autowired
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
@Controller
public class HappyController {
@Autowired //默認依賴的ClubDao 對象(Bean)必須存在
//@Autowired(required = false) 改變默認方式
@Qualifier("goodClubService")
private ClubService clubService;
// Control the people entering the Club
// do something
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
(2):@Resource
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
Class type() default java.lang.Object.class;
...
- 1
- 2
- 3
- 4
- 5
- 6
public class AnotationExp {
@Resource(name = "HappyClient")
private HappyClient happyClient;
@Resource(type = HappyPlayAno .class)
private HappyPlayAno happyPlayAno;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
3、總結
(1):相同點
@Resource的作用相當於@Autowired,均可標注在字段或屬性的setter方法上。
(2):不同點
a:提供方 @Autowired是Spring的注解,@Resource是javax.annotation注解,而是來自於JSR-250,J2EE提供,需要JDK1.6及以上。
b :注入方式 @Autowired只按照Type 注入;@Resource默認按Name自動注入,也提供按照Type 注入;
c:屬性
@Autowired注解可用於為類的屬性、構造器、方法進行注值。默認情況下,其依賴的對象必須存在(bean可用),如果需要改變這種默認方式,可以設置其required屬性為false。
還有一個比較重要的點就是,@Autowired注解默認按照類型裝配,如果容器中包含多個同一類型的Bean,那么啟動容器時會報找不到指定類型bean的異常,解決辦法是結合@Qualified注解進行限定,指定注入的bean名稱。
@Resource有兩個中重要的屬性:name和type。name屬性指定byName,如果沒有指定name屬性,當注解標注在字段上,即默認取字段的名稱作為bean名稱尋找依賴對象,當注解標注在屬性的setter方法上,即默認取屬性名作為bean名稱尋找依賴對象。
需要注意的是,@Resource如果沒有指定name屬性,並且按照默認的名稱仍然找不到依賴對象時, @Resource注解會回退到按類型裝配。但一旦指定了name屬性,就只能按名稱裝配了。
d:@Resource注解的使用性更為靈活,可指定名稱,也可以指定類型 ;@Autowired注解進行裝配容易拋出異常,特別是裝配的bean類型有多個的時候,而解決的辦法是需要在增加@Qualitied進行限定。
Spring中 @Autowired注解與@Resource注解的區別
注意點:使用@Resource也要注意添加配置文件到Spring,如果沒有配置component-scan
<context:component-scan>
<!--<context:component-scan>的使用,是默認激活<context:annotation-config>功能-->
- 1
- 2
則一定要配置 annotation-config
<context:annotation-config/>
- 1
三:@Component vs @Configuration and @Bean
1、簡單介紹
Spring的官方團隊說@Component可以替代 @Configuration注解,事實上我們看源碼也可以發現看到,如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //看這里!!!
public @interface Configuration {
String value() default "";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
雖然說可以替代但是兩個注解之間還是有區別的!
Bean注解主要用於方法上,有點類似於工廠方法,當使用了@Bean注解,我們可以連續使用多種定義bean時用到的注解,譬如用@Qualifier注解定義工廠方法的名稱,用@Scope注解定義該bean的作用域范圍,譬如是singleton還是prototype等。
Spring 中新的 Java 配置支持的核心就是@Configuration 注解的類。這些類主要包括 @Bean 注解的方法來為 Spring 的 IoC 容器管理的對象定義實例,配置和初始化邏輯。
使用@Configuration 來注解類表示類可以被 Spring 的 IoC 容器所使用,作為 bean 定義的資源。
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這和 Spring 的 XML 文件中的非常類似
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
- 1
- 2
- 3
@Bean 注解扮演了和元素相同的角色。
2、舉例說明@Component 和 @Configuration
@Configuration
public static class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
@Component
public static class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
第一個代碼正常工作,正如預期的那樣,SimpleBeanConsumer
將會得到一個單例SimpleBean的鏈接。第二個配置是完全錯誤的,因為Spring會創建一個SimpleBean的單例bean,但是SimpleBeanConsumer將獲得另一個SimpleBean實例(也就是相當於直接調用new SimpleBean() ,這個bean是不歸Spring管理的),既new SimpleBean() 實例是Spring上下文控件之外的。
3、原因總結
使用@ configuration,所有標記為@ bean的方法將被包裝成一個CGLIB包裝器,它的工作方式就好像是這個方法的第一個調用,那么原始方法的主體將被執行,最終的對象將在spring上下文中注冊。所有進一步的調用只返回從上下文檢索的bean。
在上面的第二個代碼塊中,新的SimpleBeanConsumer(simpleBean())只調用一個純java方法。為了糾正第二個代碼塊,我們可以這樣做
@Component
public static class Config {
@Autowired
SimpleBean simpleBean;
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Spring @Configuration vs @Component
基本概念:@Configuration 和@Bean
四:spring MVC模塊注解
1、web模塊常用到的注解
- @Controller :表明該類會作為與前端作交互的控制層組件,通過服務接口定義的提供訪問應用程序的一種行為,解釋用戶的輸入,將其轉換成一個模型然后將試圖呈獻給用戶。
@Controller
public class HappyController {
//do something
...
}
- 1
- 2
- 3
- 4
- 5
Spring MVC 使用 @Controller 定義控制器,它還允許自動檢測定義在類路徑下的組件(配置文件中配置掃描路徑)並自動注冊。
- @RequestMapping : 這個注解用於將url映射到整個處理類或者特定的處理請求的方法。可以只用通配符!
@Controller
@RequestMapping("/happy")
public class HappyController {
@Autowired
private HappyService happyService;
@RequestMapping(/hello/*)
public void sayHello(){
//請求為 /happy/hello/* 都會進入這個方法!
//例如:/happy/hello/123 /happy/hello/adb
//可以通過get/post 請求
}
@RequestMapping(value="/haha",method=RequestMethod.GET)
public void sayHaHa(){
//只能通過get請求
}
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
@RequestMapping 既可以作用在類級別,也可以作用在方法級別。當它定義在類級別時,標明該控制器處理所有的請求都被映射到 /favsoft 路徑下。@RequestMapping中可以使用 method 屬性標記其所接受的方法類型,如果不指定方法類型的話,可以使用 HTTP GET/POST 方法請求數據,但是一旦指定方法類型,就只能使用該類型獲取數據。
- @RequestParam :將請求的參數綁定到方法中的參數上,有required參數,默認情況下,required=true,也就是改參數必須要傳。如果改參數可以傳可不傳,可以配置required=false。
@RequestMapping("/happy")
public String sayHappy(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "age", required = true) String age) {
//age參數必須傳 ,name可傳可不傳
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- @PathVariable : 該注解用於方法修飾方法參數,會將修飾的方法參數變為可供使用的uri變量(可用於動態綁定)。
@RequestMapping(value="/happy/{dayid}",method=RequestMethod.GET)
public String findPet(@PathVariable String dayid, Model mode) {
//使用@PathVariable注解綁定 {dayid} 到String dayid
}
- 1
- 2
- 3
- 4
@PathVariable中的參數可以是任意的簡單類型,如int, long, Date等等。Spring會自動將其轉換成合適的類型或者拋出 TypeMismatchException異常。當然,我們也可以注冊支持額外的數據類型。
@PathVariable支持使用正則表達式,這就決定了它的超強大屬性,它能在路徑模板中使用占位符,可以設定特定的前綴匹配,后綴匹配等自定義格式。
- @RequestBody : @RequestBody是指方法參數應該被綁定到HTTP請求Body上。
@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body,@RequestBody User user){
//可以綁定自定義的對象類型
}
- 1
- 2
- 3
- 4
- @ResponseBody : @ResponseBody與@RequestBody類似,它的作用是將返回類型直接輸入到HTTP response body中。
@ResponseBody在輸出JSON格式的數據時,會經常用到。
@RequestMapping(value = "/happy", method =RequestMethod.POST)
@ResponseBody
public String helloWorld() {
return "Hello World";//返回String類型
}
- 1
- 2
- 3
- 4
- 5
-
@RestController :控制器實現了REST的API,只為服務於JSON,XML或其它自定義的類型內容,@RestController用來創建REST類型的控制器,與@Controller類型。@RestController就是這樣一種類型,它避免了你重復的寫@RequestMapping與@ResponseBody。
-
@ModelAttribute :@ModelAttribute可以作用在方法或方法參數上,當它作用在方法上時,標明該方法的目的是添加一個或多個模型屬性(model attributes)。
該方法支持與@RequestMapping一樣的參數類型,但並不能直接映射成請求。控制器中的@ModelAttribute方法會在@RequestMapping方法調用之前而調用。@ModelAttribute方法有兩種風格:一種是添加隱形屬性並返回它。另一種是該方法接受一個模型並添加任意數量的模型屬性。用戶可以根據自己的需要選擇對應的風格。
五:Spring事務模塊注解
1、常用到的注解
在處理dao層或service層的事務操作時,譬如刪除失敗時的回滾操作。使用@Transactional 作為注解,但是需要在配置文件激活
<!-- 開啟注解方式聲明事務 -->
<tx:annotation-driven transaction-manager="transactionManager" />
- 1
- 2
2、舉例
@Service
public class CompanyServiceImpl implements CompanyService {
@Autowired
private CompanyDAO companyDAO;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public int deleteByName(String name) {
int result = companyDAO.deleteByName(name);
return result;
}
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
3、總結
事務的傳播機制和隔離機制比較重要!
事務傳播行為類型 | 說明 |
---|---|
PROPAGATION_REQUIRED | 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。 |
PROPAGATION_SUPPORTS | 支持當前事務,如果當前沒有事務,就以非事務方式執行。 |
PROPAGATION_MANDATORY | 使用當前的事務,如果當前沒有事務,就拋出異常。 |
PROPAGATION_REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則拋出異常 |
PROPAGATION_NESTED | 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類 似的操作 |
readOnly : 事務的讀寫屬性,取true或者false,true為只讀、默認為false
rollbackFor : 回滾策略,當遇到指定異常時回滾。譬如上例遇到異常就回滾
timeout (補充的) : 設置超時時間,單位為秒
isolation : 設置事務隔離級別,枚舉類型,一共五種
類型 | 說明 |
---|---|
DEFAULT | 采用數據庫默認隔離級別 |
READ_UNCOMMITTED | 讀未提交的數據(會出現臟讀取) |
READ_COMMITTED | 讀已提交的數據(會出現幻讀,即前后兩次讀的不一樣) |
REPEATABLE_READ | 可重復讀,會出現幻讀 |
SERIALIZABLE 串行化 | (對資源消耗較大,一般不使用) |