Spring(二)--FactoryBean、bean的後置處理器、資料庫連接池、引用外部檔案、使用註解配置bean等,--factorybeanbean
實驗1:配置通過靜態Factory 方法建立的bean [通過靜態方法提供執行個體對象,工廠類本身不需要執行個體化!]
1.建立靜態工廠類
public class StaticFactory { private static HashMap<String, Book> map = null; static{ map = new HashMap<>(); map.put("book01", new Book("java", "you", 33.33)); map.put("book02", new Book("c++", "you", 66.66)); map.put("book03", new Book("c#", "you", 99.99)); } public static Book getBookById(String id){ return map.get(id); }}
工廠本身不建立對象,而是通過其提供的靜態方法擷取對象
<bean id="staticFactory" class="com.neuedu.spring.entity.StaticFactory" factory-method="getBookById"> <constructor-arg value="book02"></constructor-arg></bean>
如果不寫 <constructor-arg> 會報 method="getBookById" 構造器需要參數的錯誤所以通過<constructor-arg> 傳一個參數在 junitTest 中會擷取到 book02 中的內容
實驗2:配置通過執行個體Factory 方法建立的bean [通過執行個體方法提供執行個體對象,工廠類本身需要先建立對象!]
執行個體工廠先建立一個工廠對象,然後通過工廠對象的方法擷取一個執行個體
將靜態工廠類中的方法複製,並修改為非靜態方法 【去除static】
public class InstanceFactory { private HashMap<String, Book> map = null; { map = new HashMap<>(); map.put("book01", new Book("java", "you", 33.33)); map.put("book02", new Book("c++", "you", 66.66)); map.put("book03", new Book("c#", "you", 99.99)); } public Book getBookById(String id){ return map.get(id); }}先給工廠類本身建立一個對象<bean id="instanceFactory" class="com.neuedu.spring.entity.InstanceFactory"></bean>再給方法建立一個對象 ,bean中不用寫 class ,寫 factory-bean,依賴於 instanceFactory此時需要一個Factory 方法factory-method還需要給Factory 方法提供一個參數<constructor-arg value="book03">
<bean id="instanceFactory" class="com.neuedu.spring.entity.InstanceFactory"></bean><bean id="bookFromInstanceFactory" factory-bean="instanceFactory" factory-method="getBookById"> <constructor-arg value="book03"></constructor-arg></bean>
從執行個體中擷取book03
public void test() { Object bean = ioc.getBean("bookFromInstanceFactory"); System.out.println(bean);}
實驗3:配置FactoryBean
建立 MyFactoryBean 實現 FactoryBean<>介面返回對象就是調用 getObject() 方法
public class MyFactoryBean implements FactoryBean<Book>{ @Override public Book getObject() throws Exception { //Spring的IOC容器就是調用該方法返回的對象 return new Book("java", "you", 33.33); } @Override public Class<?> getObjectType() { //返回對象的類型 return Book.class; } @Override public boolean isSingleton() { return true; }}
<!-- 配置工廠bean --><bean id="myFactoryBean" class="com.neuedu.spring.entity.MyFactoryBean"></bean>
實驗4:測試bean的後置處理器
在bean的初始化方法【init】調用前後執行操作的專門的對象Object 代表 bean 對象,String 代表 bean 對象的 id自訂後置處理器實現該介面:BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor{ @Override public Object postProcessAfterInitialization(Object object, String beanId) throws BeansException { System.out.println("After---"+object+"---"+beanId); return object; } @Override public Object postProcessBeforeInitialization(Object object, String beanId) throws BeansException { System.out.println("Before---"+object+"---"+beanId); return object; }}
沒有 bean 對象,後置處理器不會執行
<bean id="myBeanPostProcessor" class="com.neuedu.spring.entity.MyBeanPostProcessor"></bean>
如果只這麼寫的話,是不會輸出什麼的
所以在這之前需要建立一個 bean 對象
<bean id="book" class="com.neuedu.spring.entity.Book" init-method="init"> <property name="bookName" value="java"></property> <property name="author" value="you"></property> <property name="price" value="32.32"></property></bean><bean id="myBeanPostProcessor" class="com.neuedu.spring.entity.MyBeanPostProcessor"></bean>
在 Book 類中建立 init( ) 方法,輸出“Book對象的初始化方法”以 觀察是否在對象的初始化方法之前和之後處理
bean對象屬性的檢查,屬性名稱、屬性類型是否規範
資料庫連接池: > 資料庫連接池就是存放資料庫連接(Connection)的集合> 我們擷取一個資料庫連接是一個相對很麻煩的過程, 如果我們擷取一個資料庫連接,使用一次以後就給它關閉了 下一次再去使用的時候就要重新建立一個新的資料庫連接> 所以我們提出了一個資料庫連接池的概念,資料庫連接池放的都是資料庫連接(Connection) 我們在去使用資料庫連接時候,不用再去重新建立資料庫連接,而是直接從池中擷取, 使用完的資料庫連接,也不是直接銷毀,而是要放回到串連池。 > 資料庫連接池的常見的屬性: 初始串連數量:資料連線池建立以後,儲存資料庫連接的數量 最小空閑串連數:資料庫連接池最少得未使用的資料庫連接的數量 最大空閑串連數:資料庫連接池最大閑置串連數,當閑置串連數滿了以後,將不會有其他串連進入池 每次增加串連數:當資料庫連接都被佔用以後,一次性增加的資料庫連接的個數 最大串連數:資料庫連接池的最大容量,當最大串連數飽和了,則不再建立新的資料庫連接 最大等待時間:當資料庫連接池飽和以後,等待擷取資料庫連接的時間 > 常見的資料庫連接池 - 所有的資料庫連接池都需要實現DataSource,當使用資料庫連接池是,我們便不再需要使用DriverManger擷取資料庫連接而是使用DataSource。 - Connection getConnection() - 從資料庫連接池中擷取資料庫連接對象 1.DBCP - DBCP是Apache出品的一款資料庫連接 - DBCP依賴於commons-pool - 使用DBCP需要匯入兩個jar包: commons-dbcp-1.4.jar commons-pool-1.5.5.jar - 當我們通過資料庫連接池擷取資料庫連接以後,我們所擷取到資料庫連接已經不是我們熟悉的那個Connection 資料庫連接池對Connection對象進行了封裝,它修改Connection的close()方法 再去調用close()資料庫連接將不會真的關閉,而是要放回到資料庫連接池中,供其他線程使用。 - 核心類: BasicDataSourceFactory 2.C3P0(重點) - C3P0使用的是XML作為設定檔 - 使用c3p0需要匯入一個jar包:c3p0-0.9.1.2.jar - 匯入c3p0的設定檔: 1.設定檔的名字:c3p0-cofig.xml 2.設定檔要求放到類路徑下(src) - 核心類: ComboPooledDataSource - 注意: DataSource就相當於池子,我們的資料庫連接都是從DataSource中擷取的, 如果程式中有多個DataSource的執行個體,那麼我們說你還不如不用資料庫連接池。
實驗5:引用外部屬性檔案
建立檔案 jdbc.properties properties 中都是索引值對格式
<context:property-placeholder location="classpath:jdbc.properties"/><bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property></bean>載入了設定檔的地址location="classpath:jdbc.properties" ,所以${ }能直接使用通過 bean 中的串連資訊可以直接從串連池擷取串連 bean. getConnection()
@Testpublic void test() throws SQLException { DataSource bean = ioc.getBean(DataSource.class); System.out.println(bean.getConnection());}
執行查詢操作
@Testpublic void test() throws SQLException { DataSource bean = ioc.getBean(DataSource.class); Connection conn = bean.getConnection(); String sql = "select * from student"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()){ String name = rs.getString("name"); String email = rs.getString("email"); String school = rs.getString("school"); String score = rs.getString("score"); System.out.println(name+"--"+email+"--"+school+"--"+score); }}
補充:
用註解就不用get/set方法建立一個 AController 類 ,屬性是外部檔案中的值,在類上加 Controller註解並導包【aop】加個註解 Value,註解裡用EL運算式擷取外部檔案每個值
@Controllerpublic class AController { @Value("${jdbc.username}") private String username; //${jdbc.username}將值賦給username @Value("${jdbc.password}") private String password; @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Override public String toString() { return "AController [username=" + username + ", password=" + password + ", driver=" + driver + ", url=" + url + "]"; }}
<context:component-scan base-package="com.neuedu"></context:component-scan><context:property-placeholder location="classpath:jdbc.properties"/>
@Testpublic void test01(){ AController bean = ioc.getBean(AController.class); System.out.println(bean);}
實驗6:基於XML的屬性裝配
(1)手動設定 建立兩個類,一個類中包含另一個類為屬性,
<!-- 手動裝配 --><bean id="userService" class="com.neuedu.spring.entity.UserService"></bean><bean id="userAction" class="com.neuedu.spring.entity.UserAction"> <property name="userService" ref="userService"></property></bean>
(2)自動裝配 ① byType :如果UserAction 類中的userService的屬性類型 UserService可以在IOC容器中找到,成功 與userService名字無關,只與屬性類型相關
<bean id="userService" class="com.neuedu.spring.entity.UserService"></bean><bean id="userAction" autowire="byType" class="com.neuedu.spring.entity.UserAction"></bean>
② byName:如果 UserAction 中有屬性名稱為userService 就可以在IOC容器中找到 只看名字
<bean id="userService" class="com.neuedu.spring.entity.UserService"></bean><bean id="userAction" autowire="byName" class="com.neuedu.spring.entity.UserAction"></bean>
實驗7:使用註解配置bean
(1)聲明bean的註解 @Component 將當前類聲明為IOC容器中的一個普通的組件 , @Controller 將當前類聲明為IOC容器中的一個控制器組件 ,接受web前端請求 @Service 將當前類聲明為IOC容器中的商務邏輯層組件 , @Repository 將當前類聲明為IOC容器中的一個持久化層組件, (2)使用基於註解的bean的配置,需要額外匯入一個jar包:spring-aop-4.0.0.RELEASE.jar(3)需要設定自動掃描的包
<context:component-scan base-package="com.neuedu"></context:component-scan>
三種寫法:(1)直接寫類名(2)類名首字母小寫(3)在@Controller 後 加上重定義的名字 ,如@Controller(value="asd") value 可省
@Testpublic void test(){// UserController bean = ioc.getBean(UserController.class);// Object bean = ioc.getBean("userController"); Object bean = ioc.getBean("asd"); System.out.println(bean);}
Spring根據上述註解其實並不能分辨當前類是否真的是一個Controller或Dao,因為標記的類和註解不對應也沒有語法錯誤。也就是將 controller層的註解寫成@Service 也沒錯,但在實際工作中,肯定要將專門的註解標記在對應的類上面。
實驗8:通過註解分別建立Dao、Service、Controller
分別在以下三層,依次調用下一層的方法,最後在dao層輸出一句話在Test 中觀察是否能輸出那句話
@Controllerpublic class UserController { @Autowired private UserService userService; public void getStudentInfo(){ userService.getStudentInfo(); }}
@Servicepublic class UserService { @Autowired private UserDao userDao; public void getStudentInfo() { userDao.getStudentInfo(); }}
@Repositorypublic class UserDao { public void getStudentInfo() { System.out.println("enenenenen"); }}
@Testpublic void test(){ UserController bean = ioc.getBean(UserController.class); bean.getStudentInfo();}
實驗9:使用context:exclude-filter指定掃描包時不包含的類
使用context:include-filter指定掃描包時要包含的類 expression 中寫的是掃描包時不包含的類的全類名【註解的全類名】
<context:component-scan base-package="com.neuedu"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>
base-package屬性指定一個需要掃描的基類包,Spring容器將會掃描這個基類包及其子包中的所有類 use-default-filters 預設為true ,當為false 時,代表不再使用預設的過濾器,在IOC容器中都找不到了但是include-filter 代表要掃描的包,所以在Test中可以在 IOC中找到controller
<context:component-scan base-package="com.neuedu" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>
使用的是所建類的全類名,而不是 @Controller 的全類名這樣在 IOC容器中也找不到
<context:component-scan base-package="com.neuedu"><context:exclude-filter type="aspectj" expression="com.neuedu.spring.entity.UserController"/></context:component-scan>
實驗10:使用@Autowired註解實現根據類型實現自動裝配
[1]首先檢測標記了@Autowired註解的屬性的類型[2]根據類型進行裝配[3]如果指定類型的bean不止一個,那麼根據需要被裝配的屬性的屬性名稱做id的值,尋找bean[4]如果根據id值還是沒有找到bean,可以使用 @Qualifier 註解手動指定要裝配的bean的id @Autowired 首先是根據類型來注入根據 service 的類型到 IOC中找
@Controllerpublic class UserController { @Autowired private UserService service; public void say(){ service.getStudentInfo(); }}因為 service 的類型是 UserService ,所以當 UserService有一個子類的時候根據類型就會找不到所以就會通過姓名尋找通過更改 service層的value值
@Service(value="service")public class UserService { @Autowired private UserDao userDao; public void getStudentInfo() { userDao.getStudentInfo(); }} @Qualifier 指定一個value 值,去 IOC容器中找它@Autowired(required=false) 指:就算找不到也不會報錯
@Controllerpublic class UserController { @Autowired(required=false) @Qualifier(value="BService") private UserService ser; public void say(){ ser.getStudentInfo(); }}