我爱编程

Spring学习笔记(五、Bean装配(下))

2017-06-11  本文已影响297人  鲁克巴克诗

上一篇:Spring学习笔记(四、Bean装配(上))

这篇讲解Bean管理的注解实现及例子

主要内容有:

一、classpath扫描与组件管理

元注解(Meta-annotations)

Paste_Image.png Paste_Image.png

关于注解可以参考,我之前写的一篇文章:理解java注解

类的自动检测及bean的注册

<context:annotation-config/>

<?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
       ">
      <context:annotation-config/>
</beans>
<?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
        ">

    <context:component-scan base-package="org.example"/>
</beans>

使用过滤器进行自定义扫描

<beans>  
      <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Repository"/>
      </context:component-scan>
</beans>
过滤类别 示例 说明
annotation org.example.SomeAnnotation 所有标注了SomeAnnotation的类。该类型采用目标类是否标注了某个注解进行过滤
assignable org.example.SomeClass 所有继承或扩展SomeClass的类。该类型采用目标类是否继承或扩展某个特定类进行过滤
aspectj org.example..*Service+ 所有类名以Service结束的类及继承或扩展了它们的类。该类型采用AspectJ表达式进行过滤
regex org\.example\.Default.* 所有org.example.Default包下的类。该类型采用正则表达式根据类的类名进行过滤
custom org.example.MyTypeFiflter 采用MyTypeFiflter通过代码的方式定义过滤规则。该类必须实现org.springframework.core.type.TypeFiflter接口

理解spring注解,参考:JAVA 注解的学习和对Spring注解的理解

定义BeanName(IoC容器总的beanId)

@Service("TestResourceService")
public class TestResource implements ApplicationContextAware {
//……
 }

不显式设置beanName的话,BeanNameGenerator会根据一定规则生成beanName,即类名首字母小写:

@Service
public class TestResource implements ApplicationContextAware {
//……
 }
<beans>
      <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator"/>
</beans>

作用域(Scope)

@Scope("prototype")
@Service("TestResourceService")
public class TestResource implements ApplicationContextAware {
      //……
}
<beans>
      <context:component-scan base-package="org.example"
        scope-resolver="org.example.MyScopeResolver"/>
</beans>

代理方式

<beans>
      <context:component-scan base-package="org.example"
        scope-proxy="interfaces"/>
</beans>

写个示例:

  1. IoC容器中添加<context:component-scan>:
<?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
        ">

        <context:component-scan base-package="test9"></context:component-scan>
</beans>
  1. 在test9包下新增一个BeanAnnotation类并使用@Component注解:
package test9;
import org.springframework.stereotype.Component;
@Component
public class BeanAnnotation {
    public void say(String str) {
        System.out.println("BeanAnnotation:" + str);
    }
}
  1. junit测试:
 @Test
    public void test9(){
        BeanAnnotation beanAnnotation=super.getBean("beanAnnotation");
        beanAnnotation.say("类的自动检测及bean的注册");

    }
  1. 显式指定beanName:
@Component("myBeanAnnotation")
public class BeanAnnotation {

    public void say(String str) {
        System.out.println("BeanAnnotation:" + str);
    }
}
  1. junit测试:
  @Test
    public void test9(){
        BeanAnnotation beanAnnotation=super.getBean("myBeanAnnotation");
        beanAnnotation.say("类的自动检测及bean的注册");
    }
  1. 在类中增加@Scope注解和showHashCode()方法
@Scope("prototype")
@Component("myBeanAnnotation")
public class BeanAnnotation {

    public void say(String str) {
        System.out.println("BeanAnnotation:" + str);
    }
    public void showHashCode(){
        System.out.println("HashCode:" + hashCode());
    }
}
  1. junit测试:
 @Test
    public void test9(){
        BeanAnnotation beanAnnotation=super.getBean("myBeanAnnotation");
        beanAnnotation.showHashCode();

        BeanAnnotation beanAnnotation2=super.getBean("myBeanAnnotation");
        beanAnnotation2.showHashCode();
    }
  1. 结果如下图,使用@Scope注解修改作用域成功~:


    Paste_Image.png
  2. 将@Scope的值去掉,还原它本身的样子

@Scope
@Component("myBeanAnnotation")
public class BeanAnnotation {

    public void say(String str) {
        System.out.println("BeanAnnotation:" + str);
    }
    public void showHashCode(){
        System.out.println("HashCode:" + hashCode());
    }
}
  1. 再次执行junit测试,结果如图:


    Paste_Image.png

@Required

@Component("myBeanAnnotation")
public class BeanAnnotation {

      private TestResource testResource;

      @Required
      public void setTestResource(TestResource testResource) {
        this.testResource = testResource;
      }
}

@Autowired

@Component("myBeanAnnotation")
public class BeanAnnotation {
    private TestResource testResource;

    @Autowired
    public void setTestResource(TestResource testResource) {
        this.testResource = testResource;
    }
@Component("myBeanAnnotation")
public class BeanAnnotation {
        @Autowired
        private TestResource testResource;
 
         private AutoWiringDao autoWiringDao;

        @Autowired
        public BeanAnnotation(AutoWiringDao autoWiringDao) {

    }
}
@Component("myBeanAnnotation")
public class BeanAnnotation {

        private TestResource testResource;

        @Autowired(required = false)
        public void setTestResource(TestResource testResource) {
            this.testResource = testResource;
        }
}

关于Spring自动装配可以参考:spring的自动装配

关于Autowired自动装配的示例:

  1. service层实现类:
@Service
public class TestServiceImpl implements TestService {

    //@Autowired用于成员变量(不需要写setter方法了)
    @Autowired
    private TestDao testDao;

    @Override
    public void save(String str) {
        //模拟业务操作
        System.out.println("TestServiceImpl接收参数:" + str);
        str = str + ":" + this.hashCode();

        testDao.save(str);
    }
}
  1. junit测试:
   @Test
    public void test10() {
        TestServiceImpl testService = super.getBean("testServiceImpl");
        testService.save("this is autowired");
    }
  1. 结果:


    Paste_Image.png
  2. 修改service层,将@Autowire设置在setTestDao()方法上:
@Service
public class TestServiceImpl implements TestService {
    private TestDao testDao;

    //@Autowired用于setter方法和用于变量是一个意思
    @Autowired
    public void setTestDao(TestDao testDao) {
        this.testDao = testDao;
    }

    @Override
    public void save(String str) {
        //模拟业务操作
        System.out.println("TestServiceImpl接收参数:" + str);
        str = str + ":" + this.hashCode();

        testDao.save(str);
    }
}
  1. 结果:


    Paste_Image.png
  2. 修改service层,将@Autowire设置在构造器方法上:

@Service
public class TestServiceImpl implements TestService {
    private TestDao testDao;

    //@Autowired用构造
    @Autowired
    public TestServiceImpl(TestDao testDao) {
        this.testDao = testDao;
    }

    @Override
    public void save(String str) {
        //模拟业务操作
        System.out.println("TestServiceImpl接收参数:" + str);
        str = str + ":" + this.hashCode();

        testDao.save(str);
    }
}
  1. 结果:


    Paste_Image.png

继续学习@Autowired

 @Autowired
    private List<BeanInterface> list;
 @Autowired
    private Map<String,BeanInterface> map;

数组及Map的自动注入例子

  1. 准备工作
    • 新建一个接口
package test11;
/**
 * Created by amber on 2017/6/15.
 */
public interface BeanInterface {
}
* 新建两个它的实现类BeanImplOne 、BeanImplTwo 并添加通用注解@Component
package test11;
import org.springframework.stereotype.Component;
/**
 * Created by amber on 2017/6/15.
 */
@Component
public class BeanImplOne implements BeanInterface {
}
package test11;
import org.springframework.stereotype.Component;
/**
 * Created by amber on 2017/6/15.
 */
@Component
public class BeanImplTwo implements BeanInterface {
}
package test11;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
 * Created by amber on 2017/6/15.
 */
@Component
public class BeanInvoker {
   @Autowired
    private List<BeanInterface> list;
    @Autowired
    private Map<String,BeanInterface> map;
    public void say(){
        if(list!=null){
            System.out.println("list is not null! ");
            for(BeanInterface item : list){
                System.out.println("className: "+item.getClass().getName());
            }
        }else {
            System.out.println("list is null! ");
        }
       if(map!=null){
            System.out.println("map is not null! ");
            for(Map.Entry item : map.entrySet()){
                System.out.println("key: "+item.getKey()+ " className: "+item.getValue().getClass().getName());
            }
        }else {
            System.out.println("map is null! ");
        }
   }
}
  1. 测试函数:
 @Test
    public void test11() {
        BeanInvoker invoker=super.getBean("beanInvoker");
        invoker.say();
    }
  1. 结果:


    Paste_Image.png
  2. 增加集合类型的排序,在两个实现类上使用@Order
package test11;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
 * Created by amber on 2017/6/15.
 */
@Order(2)
@Component
public class BeanImplOne implements BeanInterface {
}
package test11;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
 * Created by amber on 2017/6/15.
 */
@Order(1)
@Component
public class BeanImplTwo implements BeanInterface {
}

经过排序,BeanImplTwo 就对照@Order(1),先被加入到list中了。


Paste_Image.png

Qualifier

在xml配置文件中使用qualifier ,就相当于beanid:

 <bean class="test11.BeanImplOne">
        <qualifier value="main"/>
  </bean>

上一个测试得知BeanInterface 有两个实现类,我现在使用@Qualifier注解指定只注入其中一个:

 @Autowired
    @Qualifier("beanImplOne")
    private BeanInterface beanInterface;
  public void say(){
        if(beanInterface!=null){
            System.out.println("name: "+beanInterface.getClass().getName());
        }else {
            System.out.println("beanInterface is null! ");
        }
   }

结果:


Paste_Image.png

基于Java容器的注解@Bean

示例:

  1. 创建一个Store接口:
package test12;
/**
 * Created by amber on 2017/6/15.
 */
public interface Store {
}
  1. 创建一个它的是实现类,StringStore:
package test12;
/**
 * Created by amber on 2017/6/15.
 */
public class StringStore implements Store {
}
  1. 创建StringConfig
package test12;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * Created by amber on 2017/6/15.
 */
@Configuration
public class StringConfig {
   @Bean
    public Store getStringStore(){
        return  new StringStore();
    }
}
  1. 测试方法:
   @Test
    public void test12() {
        Store store=super.getBean("getStringStore");
        System.out.println("store name: "+store.getClass().getName());
    }

结果:


Paste_Image.png
  1. 指定@Bean(name="xx")
package test12;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * Created by amber on 2017/6/15.
 */
@Configuration
public class StringConfig {
    @Bean(name = "stringStore")
    public Store getStringStore(){
        return  new StringStore();
    }
}
 @Test
    public void test12() {
        Store store=super.getBean("stringStore");
        System.out.println("store name: "+store.getClass().getName());
    }

结果:


Paste_Image.png
  1. 指定这个bean的init和destroy方法
@Bean(name = "stringStore",initMethod="init",destroyMethod = "destroy")
    public Store getStringStore(){
        return  new StringStore();
    }

StringStore类添加init和destroy方法:

package test12;
/**
 * Created by amber on 2017/6/15.
 */
public class StringStore implements Store {
    
    public void init(){
        System.out.println("StringStore初始化");     
    }

    public void destroy(){
        System.out.println("StringStore销毁");
   }
}

运行结果:


Paste_Image.png

基于Java容器的注解@ImportResource和@Value注解进行资源文件读取并把结果注入到对应属性

<?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
        ">
          <context:component-scan base-package="test12"></context:component-scan>
          <context:property-placeholder location="classpath:jdbc.properties"/>
          <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="url" value="${db.url}"></property>
              <property name="username" value="${db.username}"></property>
              <property name="password" value="${db.password}"></property>
          </bean>
</beans>

jdbc.properties文件:

db.driverLocation=I:/IdeaProjects/mysql-connector-java-5.1.6-bin.jar
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/ambermall?characterEncoding=utf-8
db.username=root
db.password=amber
package test12;
/**
 * Created by amber on 2017/6/16.
 */
public class MyDriverManager {
    public MyDriverManager(){}
    public MyDriverManager(String url,String userName,String password){
        System.out.println("url: "+url+"\nuserName: "+userName+"\npassword: "+password);
    }
}

修改之前的StringConfig类如下:
因为我加载的是.properties文件,所以注解使用@PropertySource。如果加载xml文件,请使用@ImportResource

package test12;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
/**
 * Created by amber on 2017/6/15.
 */
@Configuration
@PropertySource("classpath:jdbc.properties")
public class StringConfig {
    @Value("${db.url}")
    private String url;
    @Value("${db.username}")
    private String userName;
    @Value("${db.password}")
    private String password;
    @Bean
    public MyDriverManager myDriverManager(){
        return new MyDriverManager(url,userName,password);
    }
}

测试方法:

  @Test
    public void test12() {
        MyDriverManager manager = super.getBean("myDriverManager");
        if(manager!=null)
        System.out.println(manager.getClass().getName());
    }

结果:


Paste_Image.png

@Bean和@Scope

下面示例:

  1. 更改StringConfig类
package test12;
import org.springframework.context.annotation.*;
/**
 * Created by amber on 2017/6/15.
 */
@Configuration
public class StringConfig {
    @Bean(name = "stringStore",initMethod = "init",destroyMethod = "destroy")
    @Scope("prototype")
    public Store stringStore() {
        return new StringStore();
    }
}
  1. 测试方法:
   @Test
    public void test12() {
        for(int i=0;i<2;i++){
            StringStore store = super.getBean("stringStore");
            System.out.println(store.hashCode());
        }
    }
  1. 结果:细心的小伙伴可能发现,多例之后,没有执行destroy的方法了,那是因为多例模式,每产生一个实例在使用完成后,都会自动被JVM垃圾回收器回收。
Paste_Image.png

基于泛型的自动装配

示例:

  1. 修改Store接口:
package test12;
/**
 * Created by amber on 2017/6/15.
 */
public interface Store<T>{
}
  1. 修改实现类StringStore :
package test12;
/**
 * Created by amber on 2017/6/15.
 */
public class StringStore implements Store<String> {
}

增加IntegerStore:

package test12;
/**
 * Created by amber on 2017/6/16.
 */
public class IntegerStore implements Store<Integer> {
}
  1. 更改StringConfig 类:
package test12;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
/**
 * Created by amber on 2017/6/15.
 */
@Configuration
public class StringConfig {
    @Autowired
    private StringStore s1;
    @Autowired
    private IntegerStore s2;
    @Bean
    public Store store() {
        System.out.println("s1: "+s1.getClass().getName());
        System.out.println("s2: "+s2.getClass().getName());
        return new StringStore();
    }
    @Bean
    public StringStore stringStore(){
        return new StringStore();
    }
    @Bean
    public IntegerStore integerStore(){
        return new IntegerStore();
    }
}
  1. 测试
  @Test
    public void test12() {
      super.getBean("store");
    }
  1. 结果:


    Paste_Image.png

CustomAutowireConfigurer

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
        <property name="customQualifierTypes">
          <set>
              <value>example.CustomQualifier</value>
          </set>
    </property>
</bean>

AutowireCandidateResolver用来处理customQualifierTypes的,通过以下的几种方式来决定自动装载的候选Bean:
* Bean定义中的autowire-candidate的值
* 任何<beans/>标签中定义的default-autowire-candidates的值
* @Qualifier注解和任何在CustomAutowireConfigurer中定义的自定义的限定符注解
* 当多个Bean限定为自动装载的候选时, 前文中提到的primary属性是优先考虑的。

@Resource

@PostConstruct 和 @PreDestroy

下面示例:

  1. 创建JsrDao 类:
package test13;
import org.springframework.stereotype.Repository;
/**
 * Created by amber on 2017/6/16.
 */
@Repository
public class JsrDao {
    public void save(){
        System.out.println("JsrDao invoked");
    }
}
  1. 创建JsrService 类:
package test13;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
 * Created by amber on 2017/6/16.
 */
@Service
public class JsrService {
//@Resource注解在成员变量上或者方法上都是一样的效果,二者选其一即可,此处为了展示才都写上了。此处也可以替换成@Autowired,效果也是一样的
    @Resource 
    private JsrDao jsrDao;
//@Resource注解在成员变量上或者方法上都是一样的效果,二者选其一即可,此处为了展示才都写上了。此处也可以替换成@Autowired,效果也是一样的
    @Resource
    public void setJsrDao(JsrDao jsrDao) {
        this.jsrDao = jsrDao;
    }
    public void save(){
        jsrDao.save();
    }
}
  1. 测试:
  @Test
    public void test13() {
        JsrService jsrService=super.getBean("jsrService");
        jsrService.save();
    }
  1. 结果:


    Paste_Image.png
  2. 在JsrService 中添加@PostConstruct 和 @PreDestroy
package test13;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
 * Created by amber on 2017/6/16.
 */
@Service
public class JsrService {
    @Resource
    private JsrDao jsrDao;
    @Resource
    public void setJsrDao(JsrDao jsrDao) {
        this.jsrDao = jsrDao;
    }
    @PreDestroy //销毁回调
    public void destroy(){
        System.out.println("我是销毁回调");
    }
    @PostConstruct //初始化回调
    public void init(){
        System.out.println("我是初始化回调");
    }
    public void save(){
        jsrDao.save();
    }
}
  1. 结果:


    Paste_Image.png

使用JSR330标准注解

        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

@Inject

@Named

下面示例:

  1. 修改JSRService类的注解@Service改成@Named
package test13;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
/**
 * Created by amber on 2017/6/16.
 */
@Named
public class JsrService {
    @Inject
    private JsrDao jsrDao;
    @Inject
    public void setJsrDao(JsrDao jsrDao) {
        this.jsrDao = jsrDao;
    }
    @PreDestroy //销毁回调
    public void destroy(){
        System.out.println("我是销毁回调");
    }
    @PostConstruct //初始化回调
    public void init(){
        System.out.println("我是初始化回调");
    }
    public void save(){
        jsrDao.save();
    }
}
  1. 运行结果:


    Paste_Image.png

下一篇:Spring学习笔记(六、Spring AOP基本概念)

上一篇 下一篇

猜你喜欢

热点阅读