Spring IOC基于XML和注解管理Bean(一)
2.9、實驗八:p命名空間
引入p命名空間
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
引入p命名空間后,可以通過以下方式為bean的各個屬性賦值
<bean id="studentSix" class="org.example.bean.Student"
p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMap-ref="teacherMap"></bean>
2.10、實驗九:引入外部屬性文件
①加入依賴
<!-- MySQL驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 數據源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
②創(chuàng)建外部屬性文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EEZ1sTcC-1686469380998)(img/2023-05-25-21-43-05.png)]
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
③引入屬性文件
引入context 名稱空間
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
<!-- 引入外部屬性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
注意:在使用 <context:property-placeholder>
元素加載外包配置文件功能前,首先需要在 XML 配置的一級標簽 <beans>
中添加 context 相關的約束。
④配置bean
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
⑤測試
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-datasource.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
2.11、實驗十:bean的作用域
①概念
在Spring中可以通過配置bean標簽的scope
屬性來指定bean的作用域范圍,各取值含義參加下表:
取值 | 含義 | 創(chuàng)建對象的時機 |
---|---|---|
singleton(默認) | 在IOC容器中,這個bean的對象始終為單實例 | IOC容器初始化時 |
prototype | 這個bean在IOC容器中有多個實例 | 獲取bean時 |
如果是在WebApplicationContext環(huán)境下還會有另外幾個作用域(但不常用):
取值 | 含義 |
---|---|
request | 在一個請求范圍內有效 |
session | 在一個會話范圍內有效 |
②創(chuàng)建類User
package org.example.bean;
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
③配置bean
<!-- scope屬性:取值singleton(默認值),bean在IOC容器中只有一個實例,IOC容器初始化時創(chuàng)建對象 -->
<!-- scope屬性:取值prototype,bean在IOC容器中可以有多個實例,getBean()時創(chuàng)建對象 -->
<bean class="org.example.bean.User" scope="prototype"></bean>
④測試
@Test
public void testBeanScope(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
User user1 = ac.getBean(User.class);
User user2 = ac.getBean(User.class);
System.out.println(user1==user2);
}
2.12、實驗十一:bean生命周期
①具體的生命周期過程
-
1、bean對象創(chuàng)建(調用無參構造器)
-
2、給bean對象設置屬性
-
3、bean的后置處理器(初始化之前)
-
4、bean對象初始化(需在配置bean時指定初始化方法)
-
5、bean的后置處理器(初始化之后)
-
6、bean對象就緒可以使用
-
7、bean對象銷毀(需在配置bean時指定銷毀方法)
-
8、IOC容器關閉
②修改類User
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
System.out.println("生命周期:1、創(chuàng)建對象");
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
System.out.println("生命周期:2、依賴注入");
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void initMethod(){
System.out.println("生命周期:3、初始化");
}
public void destroyMethod(){
System.out.println("生命周期:5、銷毀");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
注意其中的
initMethod()
和destroyMethod()
,可以通過配置bean指定為初始化和銷毀的方法
③配置bean
<!-- 使用init-method屬性指定初始化方法 -->
<!-- 使用destroy-method屬性指定銷毀方法 -->
<bean class="org.example.bean.User" scope="prototype" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="1001"></property>
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
<property name="age" value="23"></property>
</bean>
④測試
@Test
public void testLife(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ac.getBean(User.class);
System.out.println("生命周期:4、通過IOC容器獲取bean并使用");
ac.close();
}
⑤bean的后置處理器
bean的后置處理器會在生命周期的初始化前后添加額外的操作,需要實現BeanPostProcessor
接口,且配置到IOC容器中,需要注意的是,bean后置處理器不是單獨針對某一個bean生效,而是針對IOC容器中所有bean都會執(zhí)行
創(chuàng)建bean的后置處理器:
package org.example.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("☆☆☆" + beanName + " = " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("★★★" + beanName + " = " + bean);
return bean;
}
}
在IOC容器中配置后置處理器:
<!-- bean的后置處理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="org.example.process.MyBeanProcessor"/>
2.13、實驗十二:FactoryBean
①簡介
FactoryBean
是Spring提供的一種整合第三方框架的常用機制。和普通的bean不同,配置一個FactoryBean類型的bean,在獲取bean的時候得到的并不是class屬性中配置的這個類的對象,而是getObject()
方法的返回值。通過這種機制,Spring可以幫我們把復雜組件創(chuàng)建的詳細過程和繁瑣細節(jié)都屏蔽起來,只把最簡潔的使用界面展示給我們。
整合Mybatis時,Spring就是通過FactoryBean機制來幫我們創(chuàng)建SqlSessionFactory
對象的。
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
/**
* Interface to be implemented by objects used within a {@link BeanFactory} which
* are themselves factories for individual objects. If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
*
* <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
* A FactoryBean is defined in a bean style, but the object exposed for bean
* references ({@link #getObject()}) is always the object that it creates.
*
* <p>FactoryBeans can support singletons and prototypes, and can either create
* objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
* interface allows for exposing more fine-grained behavioral metadata.
*
* <p>This interface is heavily used within the framework itself, for example for
* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
* custom components as well; however, this is only common for infrastructure code.
*
* <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
* supposed to rely on annotation-driven injection or other reflective facilities.</b>
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the
* bootstrap process, even ahead of any post-processor setup. If you need access to
* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
*
* <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
* instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
* a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}
* will <i>not</i> be called automatically. Instead, a FactoryBean should implement
* {@link DisposableBean} and delegate any such close call to the underlying object.
*
* <p>Finally, FactoryBean objects participate in the containing BeanFactory's
* synchronization of bean creation. There is usually no need for internal
* synchronization other than for purposes of lazy initialization within the
* FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 08.03.2003
* @param <T> the bean type
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public interface FactoryBean<T> {
/**
* The name of an attribute that can be
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
* factory beans can signal their object type when it can't be deduced from
* the factory bean class.
* @since 5.2
*/
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* <p>As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
* <p>If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
* <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
@Nullable
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
* <p>This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
* <p>In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
* <p>This method can be called <i>before</i> this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
* <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
@Nullable
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
* <p>The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
* <p><b>NOTE:</b> This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
* <p>The default implementation returns {@code true}, since a
* {@code FactoryBean} typically manages a singleton instance.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
default boolean isSingleton() {
return true;
}
}
②創(chuàng)建類UserFactoryBean
package org.example.bean;
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
③配置bean
<bean id="user" class="org.example.bean.UserFactoryBean"></bean>
④測試
@Test
public void testUserFactoryBean(){
//獲取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-factorybean.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
2.14、實驗十三:基于xml自動裝配
自動裝配:
根據指定的策略,在IOC容器中匹配某一個bean,自動為指定的bean中所依賴的類類型或接口類型屬性賦值
①場景模擬
創(chuàng)建類UserController
package org.example.autowire.controller;
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
創(chuàng)建接口UserService
package org.example.autowire.service;
public interface UserService {
void saveUser();
}
創(chuàng)建類UserServiceImpl實現接口UserService
package org.example.autowire.service.impl;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
創(chuàng)建接口UserDao
package org.example.autowire.dao;
public interface UserDao {
void saveUser();
}
創(chuàng)建類UserDaoImpl實現接口UserDao
package org.example.autowire.dao.impl;
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
②配置bean
使用bean標簽的
autowire
屬性設置自動裝配效果自動裝配方式:
byType
byType
:根據類型匹配IOC容器中的某個兼容類型的bean,為屬性自動賦值若在IOC中,沒有任何一個兼容類型的bean能夠為屬性賦值,則該屬性不裝配,即值為默認值null
若在IOC中,有多個兼容類型的bean能夠為屬性賦值,則拋出異常NoUniqueBeanDefinitionException
<bean id="userController" class="org.example.autowire.controller.UserController" autowire="byType"></bean>
<bean id="userService" class="org.example.autowire.service.impl.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="org.example.autowire.dao.impl.UserDaoImpl"></bean>
自動裝配方式:
byName
byName
:將自動裝配的屬性的屬性名,作為bean的id在IOC容器中匹配相對應的bean進行賦值
<bean id="userController" class="org.example.autowire.controller.UserController" autowire="byName"></bean>
<bean id="userService" class="org.example.autowire.service.impl.UserServiceImpl" autowire="byName"></bean>
<bean id="userServiceImpl" class="org.example.autowire.service.impl.UserServiceImpl" autowire="byName"></bean>
<bean id="userDao" class="org.example.autowire.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoImpl" class="org.example.autowire.dao.impl.UserDaoImpl"></bean>
③測試
@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}
3、基于注解管理Bean
從 Java 5 開始,Java 增加了對注解(Annotation)的支持,它是代碼中的一種特殊標記,可以在編譯、類加載和運行時被讀取,執(zhí)行相應的處理。開發(fā)人員可以通過注解在不改變原有代碼和邏輯的情況下,在源代碼中嵌入補充信息。
Spring 從 2.5 版本開始提供了對注解技術的全面支持,我們可以使用注解來實現自動裝配,簡化 Spring 的 XML 配置。
Spring 通過注解實現自動裝配的步驟如下:
- 引入依賴
- 開啟組件掃描
- 使用注解定義 Bean
- 依賴注入
3.1、搭建子模塊spring6-ioc-annotation
①搭建模塊
搭建方式如:spring6-ioc-xml
②引入配置文件
引入spring-ioc-xml模塊日志log4j2.xml
③添加依賴
<dependencies>
<!--spring context依賴-->
<!--當你引入Spring Context依賴之后,表示將Spring的基礎依賴引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.3</version>
</dependency>
<!--junit5測試-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<!--log4j2的依賴-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
3.2、開啟組件掃描
Spring 默認不使用注解裝配 Bean,因此我們需要在 Spring 的 XML 配置中,通過 <context:component-scan>
元素開啟 Spring Beans的自動掃描功能。開啟此功能后,Spring 會自動從掃描指定的包(base-package
屬性設置)及其子包下的所有類,如果類上使用了 @Component
注解,就將該類裝配到容器中。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--開啟組件掃描功能-->
<context:component-scan base-package="org.example"></context:component-scan>
</beans>
注意:在使用 <context:component-scan>
元素開啟自動掃描功能前,首先需要在 XML 配置的一級標簽 <beans>
中添加 context 相關的約束。
情況一:最基本的掃描方式
<context:component-scan base-package="org.example">
</context:component-scan>
情況二:指定要排除的組件
<context:component-scan base-package="org.example">
<!-- context:exclude-filter標簽:指定排除規(guī)則 -->
<!--
type:設置排除或包含的依據
type="annotation",根據注解排除,expression中設置要排除的注解的全類名
type="assignable",根據類型排除,expression中設置要排除的類型的全類名
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable" expression="org.example.controller.UserController"/>-->
</context:component-scan>
情況三:僅掃描指定組件
<context:component-scan base-package="org.example" use-default-filters="false">
<!-- context:include-filter標簽:指定在原有掃描規(guī)則的基礎上追加的規(guī)則 -->
<!-- use-default-filters屬性:取值false表示關閉默認掃描規(guī)則 -->
<!-- 此時必須設置use-default-filters="false",因為默認規(guī)則即掃描指定包下所有類 -->
<!--
type:設置排除或包含的依據
type="annotation",根據注解排除,expression中設置要排除的注解的全類名
type="assignable",根據類型排除,expression中設置要排除的類型的全類名
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="org.example.controller.UserController"/>-->
</context:component-scan>
3.3、使用注解定義 Bean
Spring 提供了以下多個注解,這些注解可以直接標注在 Java 類上,將它們定義成 Spring Bean。
注解 | 說明 |
---|---|
@Component | 該注解用于描述 Spring 中的 Bean,它是一個泛化的概念,僅僅表示容器中的一個組件(Bean),并且可以作用在應用的任何層次,例如 Service 層、Dao 層等。 使用時只需將該注解標注在相應類上即可。 |
@Repository | 該注解用于將數據訪問層(Dao 層)的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Service | 該注解通常作用在業(yè)務層(Service 層),用于將業(yè)務層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Controller | 該注解通常作用在控制層(如SpringMVC 的 Controller),用于將控制層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
3.4、實驗一:@Autowired注入
單獨使用@Autowired
注解,默認根據類型裝配?!灸J是byType】
查看源碼:
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
源碼中有兩處需要注意:
-
第一處:該注解可以標注在哪里?
-
- 構造方法上
- 方法上
- 形參上
- 屬性上
- 注解上
-
第二處:該注解有一個
required
屬性,默認值是true,表示在注入的時候要求被注入的Bean必須是存在的,如果不存在則報錯。如果required屬性設置為false,表示注入的Bean存在或者不存在都沒關系,存在的話就注入,不存在的話,也不報錯。
①場景一:屬性注入
創(chuàng)建UserDao接口
package org.example.dao;
public interface UserDao {
public void print();
}
創(chuàng)建UserDaoImpl實現
package org.example.dao.impl;
import org.example.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao層執(zhí)行結束");
}
}
創(chuàng)建UserService接口
package org.example.spring6.service;
public interface UserService {
public void out();
}
創(chuàng)建UserServiceImpl實現類
package org.example.spring6.service.impl;
import org.example.spring6.dao.UserDao;
import org.example.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
創(chuàng)建UserController類
package org.exampleu.spring6.controller;
import org.example.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private UserService userService;
public void out() {
userService.out();
System.out.println("Controller層執(zhí)行結束。");
}
}
測試一
package org.example.spring6.bean;
import org.example.spring6.controller.UserController;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
private Logger logger = LoggerFactory.getLogger(UserTest.class);
@Test
public void testAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.out();
logger.info("執(zhí)行成功");
}
}
以上構造方法和setter方法都沒有提供,經過測試,仍然可以注入成功。
②場景二:set注入
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
修改UserController類
package org.example.controller;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void out() {
userService.out();
System.out.println("Controller層執(zhí)行結束。");
}
}
測試:成功調用
③場景三:構造方法注入
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
修改UserController類
package org.example.controller;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void out() {
userService.out();
System.out.println("Controller層執(zhí)行結束。");
}
}
測試:成功調用
注:在 Spring4.x 中增加了新的特性:如果類只提供了一個帶參數的構造方法,則不需要對對其內部的屬性寫 @Autowired 注解,Spring 會自動為你注入屬性。(請看場景五)
④場景四:形參上注入
修改UserServiceImpl類
package org.example.service.impl;
import org.example.spring6.dao.UserDao;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
修改UserController類
package org.example.controller;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
public void out() {
userService.out();
System.out.println("Controller層執(zhí)行結束。");
}
}
測試:成功調用
⑤場景五:只有一個構造函數,無注解
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
測試通過
當有參數的構造方法只有一個時,@Autowired注解可以省略。
說明:有多個構造方法時呢?大家可以測試(再添加一個無參構造函數),測試報錯
⑥場景六:@Autowired注解和@Qualifier注解聯合
添加dao層實現
package org.example.dao.impl;
import org.example.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoRedisImpl implements UserDao {
@Override
public void print() {
System.out.println("Redis Dao層執(zhí)行結束");
}
}
測試:測試異常
錯誤信息中說:不能裝配,UserDao這個Bean的數量等于2
怎么解決這個問題呢?當然要byName,根據名稱進行裝配了。
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl") // 指定bean的名字
private UserDao userDao;
@Override
public void out() {
userDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
總結
-
@Autowired
注解可以出現在:屬性上、構造方法上、構造方法的參數上、setter方法上。 - 當帶參數的構造方法只有一個,@Autowired注解可以省略。()
- @Autowired注解默認根據類型注入。如果要根據名稱注入的話,需要配合
@Qualifier
注解一起使用。
3.5、實驗二:@Resource注入
@Resource
注解也可以完成屬性注入。那它和@Autowired注解有什么區(qū)別?
-
@Resource
注解是JDK擴展包中的,也就是說屬于JDK的一部分。所以該注解是標準注解,更加具有通用性。(JSR-250標準中制定的注解類型。JSR是Java規(guī)范提案。) - @Autowired注解是Spring框架自己的。
- @Resource注解默認根據名稱裝配byName,未指定name時,使用屬性名作為name。通過name找不到的話會自動啟動通過類型byType裝配。
- @Autowired注解默認根據類型裝配byType,如果想根據名稱裝配,需要配合@Qualifier注解一起用。
- @Resource注解用在屬性上、setter方法上。
- @Autowired注解用在屬性上、setter方法上、構造方法上、構造方法參數上。
@Resource注解屬于JDK擴展包,所以不在JDK當中,需要額外引入以下依賴:【如果是JDK8的話不需要額外引入依賴。高于JDK11或低于JDK8需要引入以下依賴。】
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
源碼:
package jakarta.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default Object.class;
Resource.AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
public static enum AuthenticationType {
CONTAINER,
APPLICATION;
private AuthenticationType() {
}
}
}
①場景一:根據name注入
修改UserDaoImpl類
package org.example.dao.impl;
import org.example.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository("myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao層執(zhí)行結束");
}
}
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource(name = "myUserDao")
private UserDao myUserDao;
@Override
public void out() {
myUserDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
測試通過
②場景二:name未知注入
修改UserDaoImpl類
package org.example.dao.impl;
import org.example.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository("myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao層執(zhí)行結束");
}
}
修改UserServiceImpl類
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao myUserDao;
@Override
public void out() {
myUserDao.print();
System.out.println("Service層執(zhí)行結束");
}
}
測試通過
當@Resource
注解使用時沒有指定name的時候,還是根據name進行查找,這個name是 屬性名 。
③場景三 其他情況
修改UserServiceImpl類,userDao1屬性名不存在
package org.example.service.impl;
import org.example.dao.UserDao;
import org.example.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao1;
@Override
public void out() {
userDao1.print();
System.out.println("Service層執(zhí)行結束");
}
}
測試異常
根據異常信息得知:顯然當通過name找不到的時候,自然會啟動byType進行注入,以上的錯誤是因為UserDao接口下有兩個實現類導致的。所以根據類型注入就會報錯。
@Resource的set注入可以自行測試
總結:
@Resource注解:默認byName注入,沒有指定name時把屬性名當做name,根據name找不到時,才會byType注入。byType注入時,某種類型的Bean只能有一個
3.6、Spring全注解開發(fā)
全注解開發(fā)就是不再使用spring配置文件了,寫一個配置類來代替配置文件。文章來源:http://www.zghlxwxcb.cn/news/detail-483748.html
package org.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//@ComponentScan({"org.example.controller", "org.example.service","org.example.dao"})
@ComponentScan("org.example")
public class Spring6Config {
}
測試類文章來源地址http://www.zghlxwxcb.cn/news/detail-483748.html
@Test
public void testAllAnnotation(){
ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Config.class);
UserController userController = context.getBean("userController", UserController.class);
userController.out();
logger.info("執(zhí)行成功");
}
到了這里,關于Spring IOC基于XML和注解管理Bean(二)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!