基于xml的bean屬性配置:bean的定義信息與bean的實現類是分離的。
基于注解的配置:bean的定義信息是通過在bean實現類上標注注解實現。
也就是說,加了注解,相當于在XML中配置了,一樣一樣的。
一、舉個栗子:
1 package com.mesopotamia.annotation; 2 3 import org.sPRingframework.stereotype.Component; 4 5 @Component 6 public class Car { 7 private String name; 8 private String color; 9 private double price;10 public Car(){11 name="保時捷";12 color="黃色";13 }14 public String getName() {15 return name;16 }17 public void setName(String name) {18 this.name = name;19 }20 public String getColor() {21 return color;22 }23 public void setColor(String color) {24 this.color = color;25 }26 public double getPrice() {27 return price;28 }29 public void setPrice(double price) {30 this.price = price;31 }32 33 public String toString(){34 return "名字:"+name+" 顏色:"+color+" 價格:"+price;35 }36 }
第5行標注Component:spring看到這個屬性標志,會自動將Car變成容器管理類,等同于在XML中這樣配置:
1 <bean id="car" class="com.mesopotamia.annotation.Car"></bean>
二、spring啟動之前的配置?
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-3.0.xsd" 9 >10 <context:component-scan base-package="com.mesopotamia.annotation"/>11 </beans>
如上,
使用空間:context,
使用context的規則:component-scan.
第10行的意義:掃描這個包中的所有類,并從注解信息中獲取bean的基本信息(沒加注解的不掃描也不實例化)。
三、如何啟動?
按正常方式啟動即可加載(自己復習spring容器的加載),例如:
1 package com.mesopotamia.annotation; 2 3 import org.springframework.context.applicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class Main { 7 8 public static void main(String args[]){ 9 ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/annotation/beans.xml");10 Car car= ctx.getBean(Car.class);11 System.out.println(car.toString());12 }13 14 }
第10行就已經實例化Car了,并且實例化之前先跑了Car的構造函數給car的屬性附了值(因為我給構造函數里專門寫了賦值語句,沒寫的話,屬性是沒有賦值的,除非見了鬼)。
下面是打印信息,從下面第4行結果可以看到已經附了值。
1 2015-11-13 23:44:18,414 INFO [main] (AbstractApplicationContext.java:456) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@e80a59: startup date [Fri Nov 13 23:44:18 CST 2015]; root of context hierarchy2 2015-11-13 23:44:18,480 INFO [main] (XmlBeanDefinitionReader.java:315) - Loading XML bean definitions from class path resource [com/mesopotamia/annotation/beans.xml]3 2015-11-13 23:44:18,728 INFO [main] (DefaultListableBeanFactory.java:555) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@121ab80: defining beans [car,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor]; root of factory hierarchy4 名字:保時捷 顏色:黃色 價格:0.0
四、context:component-scan的配置直接選了相應包里的全部class,有沒有更多的過濾方式?
有。context:component-scan標簽下面可以再加子標簽,如:
1 <context:component-scan base-package="com.baobaotao">2 <context:include-filter type="aspectj" expression="com.baobaotao.anno.*Plugin+"/>3 <context:exclude-filter type="regex" expression="cn/.outofmemory/.spring/.[^.]+(Dao|Service)"/>
4</context:component-scan>
include-filter表示:包含哪些。type是類型,expression是表達式過濾。
exclude-filter表示:除去哪些。type是類型,expression是表達式過濾。
第二行表示:com.baobaotao.anno包下面所有以Plugin結尾的class。
第三行表示:除去所有以Dao或者Service結束的類。具體規則這里先不作討論,你要覺得你行,那你自己先啃啃里面的肉(先去復習一下正則表達式)
其他過濾表達式舉例:

五、除了Component外,其他的注解都有什么?
上面的4種起到相同的效果,都是注入bean,注入bean的四大金剛,張龍趙虎王朝馬漢,輔佐包黑炭。
5、@Autowired 成員變量或方法入參處標注,按類型匹配自動注入。
6、@Qualifier 按名稱匹配方式注入。
7、@PostConstruct指定初始化方法。(相當于XML配置中的init-method)
8、@PreDestroy指定銷毀方法。(相當于XML配置中的destroy-method)
9、@Scope 指定bean是prototype還是singleton。
六、上面這幾個注解給個具體例子說明下?
不想往下看的可以去陪女朋友了,因為重點已經講完了。下面是一個簡單登錄界面的栗子,為了突出重點,關于spring web方面的機制暫時不講,只講有關注解的部分,如果你還沒看過spring web應用部分也無妨,下面的例子只涉及注解注入部分:
XML配置文件如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context-3.0.xsd12 http://www.springframework.org/schema/tx 13 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd14 http://www.springframework.org/schema/aop15 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">16 17 <!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 -->18 <context:component-scan base-package="com.baobaotao.dao"/>19 <context:component-scan base-package="com.baobaotao.service"/>20 <context:component-scan base-package="com.baobaotao.domain"/>21 22 <!-- 配置數據源 -->23 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"24 destroy-method="close" 25 p:driverClassName="com.MySQL.jdbc.Driver"26 p:url="jdbc:mysql://localhost:3306/sampledb" 27 p:username="root"28 p:passWord="2009118293zjl" />29 30 <!-- 配置Jdbc模板 -->31 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"32 p:dataSource-ref="dataSource" />33 34 <!-- 配置事務管理器 -->35 <bean id="transactionManager"36 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"37 p:dataSource-ref="dataSource" />38 39 <!-- 通過AOP配置提供事務增強,讓service包下所有Bean的所有方法擁有事務 -->40 <aop:config proxy-target-class="true">41 <aop:pointcut id="serviceMethod"42 expression=" execution(* com.baobaotao.service..*(..))" />43 <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />44 </aop:config>45 <tx:advice id="txAdvice" transaction-manager="transactionManager">46 <tx:attributes>47 <tx:method name="*" />48 </tx:attributes>49 </tx:advice>50 </beans>
重點第18到20行:spring只掃描配置路徑下所有帶注解的地方來注入相關數據。其他行先不看,你想看我也不攔你。
WEB應用后臺java分三層:control、service、dao層,簡單講一下,直接與jsp交互的就是control層,直接操作數據庫的是dao層(持久層,數據庫里的數據都是持久性的嘛),service層負責把dao層數據拿到,經過處理,返回給control層。control層拿到數據再交給jsp前臺。(dao就是程序員,service就是老板,control就是客戶,程序員做東西,客戶使用,而老板是橋梁)
下面是control層:
1 package com.baobaotao.web; 2 3 import java.util.Date; 4 5 import javax.annotation.PostConstruct; 6 import javax.annotation.PreDestroy; 7 import javax.servlet.http.HttpServletRequest; 8 9 10 import org.apache.commons.logging.Log;11 import org.apache.commons.logging.LogFactory;12 import org.springframework.beans.factory.annotation.Autowired;13 import org.springframework.stereotype.Controller;14 import org.springframework.web.bind.annotation.RequestMapping;15 import org.springframework.web.servlet.ModelAndView;16 17 import com.baobaotao.domain.User;18 import com.baobaotao.service.UserService;19 20 @Controller21 public class LoginController{22 Log log=LogFactory.getLog(LoginController.class);23 24 @Autowired(required=false)25 private UserService userService;26 27 @RequestMapping(value = "/loginCheck.do")28 public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){29 boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(),loginCommand.getPassword());30 if (!isValidUser) {31 return new ModelAndView("login", "error", "用戶名或密碼錯誤。");32 } else {33 User user = userService.findUserByUserName(loginCommand34 .getUserName());35 user.setLastip(request.getLocalAddr());36 user.setLastVisit(new Date());37 userService.loginSuccess(user);38 request.getsession().setAttribute("user", user);39 40 return new ModelAndView("main");41 }42 }43 44 @PostConstruct45 public void init(){46 log.info("本應用程序正式拉開帷幕。。。");47 }48 49 @PreDestroy50 public void destroy(){51 log.info("本應用程序正式落幕,嚇嚇儂。。。");52 }53 }
這里用到了幾個注解,
spring看到@Controller會如同看到@Component一樣,轉換成spring容器管理的bean。
spring看到@Autowired(required=false)會自動注入(實例化)下面的userService。(直接用就行,就不用你來new一個對象啦)。
spring看到@PostConstruct就會在實例化本Bean后接著執行這個方法;
spring看到@PreDestroy就會在銷毀之前執行下面的方法;
你的眼神告訴我你不相信我。好的,咱們看日志:
tomcat啟動的時候spring容器會自動加載,下面是tomcat啟動的日志:
1 。。。。。。 2 3 2015-11-15 21:24:31,673 DEBUG [main] (DefaultSingletonBeanRegistry.java:214) - Creating shared instance of singleton bean 'userDao' 4 2015-11-15 21:24:31,674 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:430) - Creating instance of bean 'userDao' 5 2015-11-15 21:24:31,674 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 6 7 。。。。。。 8 9 2015-11-15 21:24:31,676 DEBUG [main] (InjectionMetadata.java:82) - Processing injected method of bean 'userDao': AutowiredFieldElement for private org.springframework.jdbc.core.JdbcTemplate com.baobaotao.dao.UserDao.jdbcTemplate10 2015-11-15 21:24:31,677 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'jdbcTemplate'11 2015-11-15 21:24:31,677 DEBUG [main] (AutowiredAnnotationBeanPostProcessor.java:420) - Autowiring by type from bean name 'userDao' to bean named 'jdbcTemplate'12 13 。。。。。。14 15 2015-11-15 21:24:31,678 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'userDao'16 17 18 。。。。。。19 20 2015-11-15 21:24:31,679 DEBUG [main] (DefaultSingletonBeanRegistry.java:214) - Creating shared instance of singleton bean 'userService'21 2015-11-15 21:24:31,679 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:430) - Creating instance of bean 'userService'22 2015-11-15 21:24:31,679 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'23 2015-11-15 21:24:31,680 INFO [main] (UserService.java:18) - 實例化了UserService...24 25 。。。。。。26 27 2015-11-15 21:24:31,683 DEBUG [main] (InjectionMetadata.java:82) - Processing injected method of bean 'userService': AutowiredFieldElement for private com.baobaotao.dao.UserDao com.baobaotao.service.UserService.userDao28 2015-11-15 21:24:31,685 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'userDao'29 2015-11-15 21:24:31,685 DEBUG [main] (AutowiredAnnotationBeanPostProcessor.java:420) - Autowiring by type from bean name 'userService' to bean named 'userDao'30 2015-11-15 21:24:31,686 DEBUG [main] (InjectionMetadata.java:82) - Processing injected method of bean 'userService': AutowiredFieldElement for private com.baobaotao.dao.LoginLogDao com.baobaotao.service.UserService.loginLogDao31 2015-11-15 21:24:31,686 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'loginLogDao'32 2015-11-15 21:24:31,687 DEBUG [main] (AutowiredAnnotationBeanPostProcessor.java:420) - Autowiring by type from bean name 'userService' to bean named 'loginLogDao'33 34 。。。。。。35 36 2015-11-15 21:24:32,093 INFO [main] (UserService.java:18) - 實例化了UserService...37 2015-11-15 21:24:32,094 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'userService'38 2015-11-15 21:24:32,094 DEBUG [main] (DefaultSingletonBeanRegistry.java:214) - Creating shared instance of singleton bean 'loginLog'39 40 。。。。。。41 42 015-11-15 21:24:32,252 INFO [main] (DefaultListableBeanFactory.java:555) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c272bc: defining beans [loginController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@691dee43 2015-11-15 21:24:32,252 DEBUG [main] (DefaultSingletonBeanRegistry.java:214) - Creating shared instance of singleton bean 'loginController'44 2015-11-15 21:24:32,254 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:430) - Creating instance of bean 'loginController'45 2015-11-15 21:24:32,255 INFO [main] (LoginController.java:25) - 實例化了LoginController...46 2015-11-15 21:24:32,264 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:200) - Found init method on class [com.baobaotao.web.LoginController]: public void com.baobaotao.web.LoginController.init()47 2015-11-15 21:24:32,265 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:208) - Found destroy method on class [com.baobaotao.web.LoginController]: public void com.baobaotao.web.LoginController.destroy()48 2015-11-15 21:24:32,265 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:251) - Found init method on class [com.baobaotao.web.LoginController]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@31651049 2015-11-15 21:24:32,265 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:259) - Found destroy method on class [com.baobaotao.web.LoginController]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@5cd39ffa50 2015-11-15 21:24:32,266 DEBUG [main] (InjectionMetadata.java:59) - Found injected element on class [com.baobaotao.web.LoginController]: AutowiredFieldElement for private com.baobaotao.service.UserService com.baobaotao.web.LoginController.userService51 2015-11-15 21:24:32,267 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:504) - Eagerly caching bean 'loginController' to allow for resolving potential circular references52 2015-11-15 21:24:32,268 DEBUG [main] (InjectionMetadata.java:82) - Processing injected method of bean 'loginController': AutowiredFieldElement for private com.baobaotao.service.UserService com.baobaotao.web.LoginController.userService53 2015-11-15 21:24:32,269 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'userService'54 2015-11-15 21:24:32,270 DEBUG [main] (AutowiredAnnotationBeanPostProcessor.java:420) - Autowiring by type from bean name 'loginController' to bean named 'userService'55 2015-11-15 21:24:32,271 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:291) - Invoking init method on bean 'loginController': public void com.baobaotao.web.LoginController.init()56 2015-11-15 21:24:32,271 INFO [main] (LoginController.java:50) - 本應用程序正式拉開帷幕。。。57 2015-11-15 21:24:32,271 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'loginController'58 59 。。。。。。
第44、45行是實例化LoginController,
第46/47行是根據@PostContruct和@PreDestroy發現了我們定義的初始化和銷毀方法。
第53行,spring直接給了我們一個userService對象(因為它看到了@Autowired,required=false是說如果找不到對應的bean不報異常)。
第56行,spring看到@PostContruct執行了初始化方法。
當我把tomcat停掉,spring容器就會銷毀,那么是不是要執行@PreDestroy下面的銷毀方法?當然,你要相信科學,看停掉tomcat的日志:
。。。。。5 2015-11-15 21:53:15,763 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:303) - Invoking destroy method on bean 'loginController': public void com.baobaotao.web.LoginController.destroy()6 2015-11-15 21:53:15,763 INFO [main] (LoginController.java:55) - 本應用程序正式落幕,嚇嚇儂。。。7 2015-11-15 21:53:15 org.apache.catalina.core.ApplicationContext log8 信息: Closing Spring root WebApplicationContext9 2015-11-15 21:53:15,764 INFO [main] (AbstractApplicationContext.java:1002) - Closing Root WebApplicationContext: startup date [Sun Nov 15 21:24:29 CST 2015]; root of context hierarchy
第六行果然執行了銷毀方法。
請解釋前面那個日志里第53行的Returning cached instance of singleton bean 'userService'。
是這樣的,先看UserService的部分代碼:
1 @Service2 public class UserService {3 private static Log log=LogFactory.getLog(UserService.class);4 public UserService(){5 log.info("實例化了UserService...");6 }
我在這個類中注釋了@Service,spring在啟動的時候跟@Component、@Controller、@Repository一樣,會自動把bean信息注入容器中(即把bean在spring容器中實例化),也就是說,當spring看到@Service時,會自動返回一個提前實例化好的UserService,而只要UserService上面不顯式標注:@Scope("prototype"),那么默認UserService就是單例模式的。單例模式下,spring會把bean放到IoC容器的緩存池中,而把bean的引用返回給調用者。這就是Returning cached instance of singleton bean 'userService'這句話的意義。
好的,我把UserService的注解改成下面的方式:
1 @Service2 @Scope("prototype")3 public class UserService {4 private static Log log=LogFactory.getLog(UserService.class);5 public UserService(){6 log.info("實例化了UserService...");7 }8
那么啟動tomcat打印日志如下:
1 2015-11-15 22:11:09,723 DEBUG [main] (Cglib2AopProxy.java:755) - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.aopalliance.aop.Advice) 2 2015-11-15 22:11:09,724 DEBUG [main] (Cglib2AopProxy.java:755) - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.springframework.aop.Advisor) 3 2015-11-15 22:11:09,724 DEBUG [main] (Cglib2AopProxy.java:755) - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass() 4 2015-11-15 22:11:09,760 INFO [main] (UserService.java:20) - 實例化了UserService... 5 2015-11-15 22:11:09,760 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'userService' 6 2015-11-15 22:11:09,761 DEBUG [main] (AutowiredAnnotationBeanPostProcessor.java:420) - Autowiring by type from bean name 'loginController' to bean named 'userService' 7 2015-11-15 22:11:09,761 DEBUG [main] (InitDestroyAnnotationBeanPostProcessor.java:291) - Invoking init method on bean 'loginController': public void com.baobaotao.web.LoginController.init() 8 2015-11-15 22:11:09,761 INFO [main] (LoginController.java:50) - 本應用程序正式拉開帷幕。。。 9 2015-11-15 22:11:09,761 DEBUG [main] (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'loginController'10 2015-11-15 22:11:09,761 DEBUG [main] (AbstractBeanFactory.java:242) - Returning cached instance of singleton bean 'org.springframework.context.annotati
你會看到,在拉開帷幕之前,又實例化了一次UserService,這就是prototype的作用。
再看:
1 @Controller 2 public class LoginController{ 3 Log log=LogFactory.getLog(LoginController.class); 4 5 public LoginController(){ 6 log.info("實例化了LoginController..."); 7 } 8 9 @Autowired(required=false)10 @Qualifier("service")11 private UserService userService;12
第10行,我在userService上面加了 @Qualifier("service")的注釋,這個表示,我引用的是名字為service的bean的實例,那么你要給UserService起個名字叫service:
1 @Service("service")2 @Scope("prototype")3 public class UserService {4 private static Log log=LogFactory.getLog(UserService.class);5 public UserService(){6 log.info("實例化了UserService...");7 }8
像第一行那樣。(Component、Controller、Repository也是那樣命名,如果是基于XML的注入如何命名?自己復習去。)
不加的話,系統找不到對應的bean,就會報錯。
spring容器的三座大山:XML配置文件(或者注解配置、bean配置等),spring容器,bean類。不管是基于XML還是基于注解,無非就是為了把bean信息注入到spring容器中,南拳和北腿,各有千秋。下面是二者的對比:

本節內容就講到這里。
吾生也有涯,而知也無涯。
——《莊子》
新聞熱點
疑難解答