zoukankan      html  css  js  c++  java
  • Spring(十五):通过注解配置 Bean

    在ClassPath中扫描组件

    1)组件扫描(component scanning):Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件;

    2)特定组件包含:

    --- @Component:基本注解,标识了一个受Spring管理的组件;

    --- @Respository:标识持久层组件;

    --- @Service:标识服务层(业务层)组件;

    --- @Controller:标识表现层组件

    3)对于扫描到的组件,Spring有默认的命名策略:使用非限定类名,第一个字母小写。也可以在注解中通过value属性值标识组件的别名。

    4)当在组件上使用了特定的注解之后,还需要在Spring的配置文件中声明<context:component-scan>:

    --- base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类。

    --- 当需要扫描多个包时,可以使用逗号分隔。

    --- 如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:

        <context:component-scan base-package="com.dx.spring.bean.componentscan"
            resource-pattern="autowire/*.class"></context:component-scan>

    --- <content:include-filter>子节点表示要包含的目标类

    --- <content:exclude-filter>子节点表示要排除在外的目标类

    --- <content:component-scal>下可以拥有若干个<content:include-filter>、<content:exclude-filter>子节点。

    Filter Type Examples Expression Description
    annotation org.example.SomeAnnotation 符合SomeAnnoation的target class
    assignable org.example.SomeClass 指定class或interface的全名
    aspectj org.example..*Service+ AspectJ語法
    regex org.example.Default.* Regelar Expression
    custom org.example.MyTypeFilter Spring3新增自訂Type,實作org.springframework.core.type.TypeFilter

    5)注意使用bean注解方式时,需要依赖spring-aop-xxx.jar包,否则会抛出:Caused by: java.lang.ClassNotFoundException: org.springframework.aop.TargetSource

    注解配置Bean示例:

    第一步:新建java project,导入spring依赖包:

    第二步:新建Componet,Repository,Service,Controller组件类:

    User.java ---Component

    package com.dx.spring.beans.annotation.component;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class User {
    
    }

    IUserRepository.java,UserRepositoryImpl.java --- Repository

    package com.dx.spring.beans.annotation.repository;
    
    public interface IUserRepository {
    
    }
    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.stereotype.Repository;
    
    @Repository(value="userRepository")
    public class UserRepositoryImpl implements IUserRepository {
    
    }

    IUserService.java,UserServiceImpl.java --- Service

    package com.dx.spring.beans.annotation.service;
    
    public interface IUserService {
    
    }
    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.stereotype.Service;
    
    @Service(value="userService")
    public class UserServiceImpl implements IUserService {
    
    }

    UserController.java ---Controller

    package com.dx.spring.beans.annotation.controller;
    
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
    
    }

    第三步:新建测试类和Spring Bean配置文件:

    Client.java测试类:

    package com.dx.spring.beans.annotation;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.dx.spring.beans.annotation.component.User;
    import com.dx.spring.beans.annotation.controller.UserController;
    import com.dx.spring.beans.annotation.repository.UserRepositoryImpl;
    import com.dx.spring.beans.annotation.service.UserServiceImpl;
    
    public class Client {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-component-scan.xml");
            User user = (User) ctx.getBean("user");
            System.out.println(user);
    
            UserRepositoryImpl userRepository = (UserRepositoryImpl) ctx.getBean("userRepository");
            System.out.println(userRepository);
    
            UserServiceImpl userService = (UserServiceImpl) ctx.getBean("userService");
            System.out.println(userService);
    
            UserController userController = (UserController) ctx.getBean("userController");
            System.out.println(userController);
        }
    }

    新建spring bean配置文件bean-component-scan.xml

    <?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-4.3.xsd">
        
        <context:component-scan base-package="com.dx.spring.beans.annotation"></context:component-scan>
    
    </beans>

    测试类执行打印信息如下:

    测试resource-pattern

    第一步:修改spring bean配置文件:

    给spring bean配置文件中<context:component-scan>节点添加属性resource-pattern="repository/*.class",使得其只监控到包名包含repository的组件

    <?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-4.3.xsd">
    
        <context:component-scan base-package="com.dx.spring.beans.annotation"
            resource-pattern="repository/*.class"></context:component-scan>
    
    </beans>

    第二步执行Client.java测试:

    发现UserRepositoryImpl userRepository = (UserRepositoryImpl) ctx.getBean("userRepository");可以获取到bean,其他组件都没有可以从ioc容器中获取到bean。

    测试context:exclude-filter

    第一步:修改spring bean配置文件:

        <context:component-scan base-package="com.dx.spring.beans.annotation">
            <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository" />
        </context:component-scan>

    备注:exclude-filter意思是不加载配置项,其他项都可以包含。

    第二步:测试结果:

    除了UserRepositoryImpl userRepository = (UserRepositoryImpl) ctx.getBean("userRepository");不可以获取到bean,其他组件都可以从ioc容器中获取到bean。

    测试context:include-filter

    第一步:修改spring bean配置文件:

        <context:component-scan base-package="com.dx.spring.beans.annotation"
            use-default-filters="false">
            <context:include-filter type="annotation"
                expression="org.springframework.stereotype.Repository" />
        </context:component-scan>

    备注:1)include-filter意思是加载配置项,其他项要依赖于use-default-filters属性值,该属性值为true都可以加载;false时,才不加载配置项以外的组件。

               2)use-default-filters="false",必须修改为false,默认值为true。

    第二步:测试结果:

    除了UserRepositoryImpl userRepository = (UserRepositoryImpl) ctx.getBean("userRepository");可以获取到bean,其他组件都不可以从ioc容器中获取到bean。

    组件装配:

    <context:component-scan>元素还会自动注册AutowiredAnnotationBeanPostPorcessor实例,该实例可以自动装备具有@AutoWired和@Resource、@Inject注解的属性。

    @Autowire注解也可以应用在数组类型的属性上,此时Spring会把所有匹配的Bean进行自动装配;

    @Autowire注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,让后自动装配所有与之兼容的Bean。

    @Autowire注解用在java.util.Map上时,若该Map的键值为String。那么,Spring将自动装配与之Map值类型兼容的Bean,此时Bean的名称作为键值。

    Map依赖注入:

    @Autowired  
    private Map<String, BaseService> map;

    这样会注入:key是bean名字;value就是所有实现了BaseService的Bean,假设使用上一篇的例子,则会得到:

    {organizationService=com.sishuok.spring4.service.OrganizationService@617029,userService=com.sishuok.spring4.service.UserService@10ac73b}

    List/数组注入:

    @Autowired  
    private List<BaseService> list; 

    这样会注入所有实现了BaseService的Bean;但是顺序是不确定的,如果我们想要按照某个顺序获取;在Spring4中可以使用@Order或实现Ordered接口来实现,如:

    @Order(value = 1)  
    @Service  
    public class UserService extends BaseService<User> {  
    }  

    @Lazy可以延迟依赖注入:

    @Lazy  
    @Service  
    public class UserService extends BaseService<User> {  
    } 
    @Lazy  
    @Autowired  
    private UserService userService; 

    可以把@Lazy放在@Autowired之上,即依赖注入也是延迟的;当我们调用userService时才会注入。即延迟依赖注入到使用时。同样适用于@Bean。

    @Conditional

    @Conditional类似于@Profile(一般用于如我们有开发环境、测试环境、正式机环境,为了方便切换不同的环境可以使用@Profile指定各个环境的配置,然后通过某个配置来开启某一个环境,方便切换,但是@Conditional的优点是允许自己定义规则。可以指定在如@Component、@Bean、@Configuration等注解的类上,以绝对Bean是否创建等。首先来看看使用@Profile的用例,假设我们有个用户模块:

    1、在测试/开发期间调用本机的模拟接口方便开发;

    2、在部署到正式机时换成调用远程接口;

    public abstract class UserService extends BaseService<User> {  
    }  
      
    @Profile("local")  
    @Service  
    public class LocalUserService extends UserService {  
    }  
      
    @Profile("remote")  
    @Service  
    public class RemoteUserService extends UserService {  
    }  

    我们在写测试用例时,可以指定我们使用哪个Profile:

    @ActiveProfiles("remote")  
    @RunWith(SpringJUnit4ClassRunner.class)  
    @ContextConfiguration(locations =  "classpath:spring-config.xml")  
    public class ServiceTest {  
      
        @Autowired  
        private UserService userService;  
    } 

    这种方式非常简单。如果想自定义如@Profile之类的注解等,那么@Conditional就派上用场了;假设我们系统中有好多本地/远程接口,那么我们定义两个注解@Local和@Remote注解要比使用@Profile方便的多;如:

    @Retention(RetentionPolicy.RUNTIME)  
    @Target({ElementType.TYPE, ElementType.METHOD})  
    @Conditional(CustomCondition.class)  
    public @interface Local {  
    }  
      
    @Retention(RetentionPolicy.RUNTIME)  
    @Target({ElementType.TYPE, ElementType.METHOD})  
    @Conditional(CustomCondition.class)  
    public @interface Remote {  
    }  
    public class CustomCondition implements Condition {  
      
        @Override  
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  
            boolean isLocalBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Local");  
            boolean isRemoteBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Remote");  
            //如果bean没有注解@Local或@Remote,返回true,表示创建Bean  
            if(!isLocalBean && !isRemoteBean) {  
                return true;  
            }  
      
            boolean isLocalProfile = context.getEnvironment().acceptsProfiles("local");  
      
            //如果profile=local 且 bean注解了@Local,则返回true 表示创建bean;  
            if(isLocalProfile) {  
                return isLocalBean;  
            }  
      
            //否则默认返回注解了@Remote或没有注解@Remote的Bean  
            return isRemoteBean;  
        }  
    }  

    然后我们使用这两个注解分别注解我们的Service:

    @Local  
    @Service  
    public class LocalUserService extends UserService {  
    }  
    
    @Remote  
    @Service  
    public class RemoteUserService extends UserService {  
    }  

    首先在@Local和@Remote注解上使用@Conditional(CustomCondition.class)指定条件,然后使用@Local和@Remote注解我们的Service,这样当加载Service时,会先执行条件然后判断是否加载为Bean。@Profile就是这样实现的,其Condition是:org.springframework.context.annotation.ProfileCondition。可以去看下源码,很简单。

    用法示例:

    spring bean配置文件:

    <?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-4.3.xsd">
    
        <context:component-scan base-package="com.dx.spring.beans.annotation">
        </context:component-scan>
    
    </beans>

    在UserController.java中注入UserService

    package com.dx.spring.beans.annotation.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    import com.dx.spring.beans.annotation.service.IUserService;
    
    @Controller
    public class UserController {
        @Autowired
        private IUserService userService;
    
        public void add() {
            System.out.println("UserController add ");
            userService.add();
        }
    }

    UserService中注入UserRepository

    package com.dx.spring.beans.annotation.service;
    
    public interface IUserService {
        void add();
    }
    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.dx.spring.beans.annotation.repository.IUserRepository;
    
    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        @Autowired
        private IUserRepository userRepository;
    
        @Override
        public void add() {
            System.out.println("UserService add...");
            userRepository.add();
        }
    }
    package com.dx.spring.beans.annotation.repository;
    
    public interface IUserRepository {
        void add();
    }
    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.stereotype.Repository;
    
    @Repository(value = "userRepository")
    public class UserRepositoryImpl implements IUserRepository {
        @Override
        public void add() {
            System.out.println("UserRepository add");
        }
    }

    Client.java调用测试:

    package com.dx.spring.beans.annotation;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.dx.spring.beans.annotation.controller.UserController;
    
    public class Client {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-component-scan.xml");
            
            UserController userController = (UserController) ctx.getBean("userController");
            System.out.println(userController);
            userController.add();
        }
    }

    测试1:实现同一个接口的多个Bean注入

    添加UserOracleRepositoryImpl.java

    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserOracleRepositoryImpl implements IUserRepository {
        @Override
        public void add() {
            System.out.println("UserOracleRepositoryImpl add");
        }
    }

    此时,从新测试执行Client.java,执行通过。UserServiceImpl.java并未抛出异常,一般情况来讲:

    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        @Autowired
        private IUserRepository userRepository;
        。。。
    }

    这里的userRepository应该包含两个实现类,那么这里是如何成功找到它对应的Bean的呢?

    按照userRepository的名称获取而来,因为UserRepositoryImpl.java的注册@Repository(value="userRepository"),按照这个名字而来。

    测试2:使用set方法注入Bean

    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.dx.spring.beans.annotation.repository.IUserRepository;
    
    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        
        private IUserRepository userRepository;
        @Autowired
        public void setUserRepository(IUserRepository userRepository) {
            this.userRepository = userRepository;
        }
        @Override
        public void add() {
            System.out.println("UserService add...");
            userRepository.add();
        }
    }

    测试3:注入Bean@Autowire上设置属性required=false

    修改User为非组件类:

    package com.dx.spring.beans.annotation.component;
    
    import org.springframework.stereotype.Component;
    
    //@Component
    public class User {
    
    }

    修改UserRepositoryImpl.java注入User:

    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import com.dx.spring.beans.annotation.component.User;
    
    @Repository(value = "userRepository")
    public class UserRepositoryImpl implements IUserRepository {
        @Autowired(required = false)
        private User user;
    
        @Override
        public void add() {
            System.out.println("UserRepository add " + user);
        }
    }

    测试通过,打印信息为:

    com.dx.spring.beans.annotation.controller.UserController@166fa74d
    UserController add
    UserService add...
    UserRepository add null

    测试4:注入Bean@Qualifier解决一个接口多个实现Bean的问题

    修改UserRepositoryImpl.java

    package com.dx.spring.beans.annotation.repository;
    
    public interface IUserRepository {
        void add();
    }
    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import com.dx.spring.beans.annotation.component.User;
    
    @Repository
    public class UserRepositoryImpl implements IUserRepository {
        @Override
        public void add() {
            System.out.println("UserRepository add " + user);
        }
    }
    package com.dx.spring.beans.annotation.repository;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserOracleRepositoryImpl implements IUserRepository {
        @Override
        public void add() {
            System.out.println("UserOracleRepositoryImpl add");
        }
    }

    在UserServiceImpl.java中注入UserRepository:

    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    import com.dx.spring.beans.annotation.repository.IUserRepository;
    
    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        
        private IUserRepository userRepository;
        @Autowired
        public void setUserRepository(IUserRepository userRepository) {
            this.userRepository = userRepository;
        }
        @Override
        public void add() {
            System.out.println("UserService add...");
            userRepository.add();
        }
    }

    此时执行就会抛出异常:

    Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: 
    Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService';
    nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService':
    Unsatisfied dependency expressed through method 'setUserRepository' parameter 0;
    nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
    No qualifying bean of type 'com.dx.spring.beans.annotation.repository.IUserRepository' available: expected at least 1 bean which qualifies as autowire candidate.
    Dependency annotations: {}

    解决方案:使用@Qualifier(value="userRepositoryImpl"),指定具体注入的Bean

    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    import com.dx.spring.beans.annotation.repository.IUserRepository;
    
    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        
        private IUserRepository userRepository;
        @Autowired
        @Qualifier(value="userRepositoryImpl")
        public void setUserRepository(IUserRepository userRepository) {
            this.userRepository = userRepository;
        }
        @Override
        public void add() {
            System.out.println("UserService add...");
            userRepository.add();
        }
    }

    此时,@Qualifier(value="userRepositoryImpl")也可以注入在set函数入参上:

    package com.dx.spring.beans.annotation.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    import com.dx.spring.beans.annotation.repository.IUserRepository;
    
    @Service(value = "userService")
    public class UserServiceImpl implements IUserService {
        
        private IUserRepository userRepository;
        @Autowired
        public void setUserRepository(@Qualifier(value="userRepositoryImpl") IUserRepository userRepository) {
            this.userRepository = userRepository;
        }
        @Override
        public void add() {
            System.out.println("UserService add...");
            userRepository.add();
        }
    }
  • 相关阅读:
    ionic+cordova开发!
    npm安装出错的时候,如何使用国内的镜像!--解决办法
    wamp环境解决局域网不能访问的问题!
    flex弹性布局属性详解!
    JS判断当前是否是IE浏览器,并返回时IE几?
    thinkphp---手机访问切换模板!
    thinkCMF的使用!
    thinkphp5在集成环境目录访问权限问题
    微信小程序 --- 下拉刷新上拉加载
    jQuery --- 利用a标签的download属性下载文件!
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/9096468.html
Copyright © 2011-2022 走看看