Spring----Bean装配

一、Bean种类

1、普通Bean

  <bean id="" class="A">  ;spring直接创建A实例对象并且返回

2、特殊Bean

  如果一个Bean实现了 FactoryBean接口,那他就是一个特殊的Bean,当spring实例化这个bean的时候,会调用 getObject() 方法,返回 getObject() 方法返回的值

  这种特殊的Bean,我们一般称为FactoryBean,例如ProxyFactoryBean,表示生产proxy的Bean

       <bean id="" class="proxyFactoryBean">,获得的是proxyBean。(getObject()方法返回的是proxy)

补充:

  FactoryBean:具有工厂生产对象的能力,但是只能生产特定的对象(典型例子SqlSessionFactoryBean),目的就是隐藏复杂的bean的配置

//举一个例子
public class UserFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        //进行复杂的配置
        User user = new User();
        return user;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}

  BeanFactory:生产Bean的工厂,可以生产任意Bean。

二、基于XML装配Bean

1、实例化方式

1.1默认构造

<bean id="" class="">  //必须提供构造方法

1.2静态工厂

facatory.class

public  class ServiceFactory {
    public static Service createService(){
        return new ServiceImpl();
    }
}

service.class

public interface Service {
    public void save();
}

serviceImpl.class

public class SaveDaoImpl implements SaveDao {
    public void save(){
        System.out.println("保存完毕");
    }
}

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.xsd">

        <!--createService 生产对象的方法-->
        <bean id="serviceFactory" class="com.test.ServiceFactory" factory-method="createService"></bean>
</beans>

测试

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        Service service = applicationContext.getBean("serviceFactory",Service.class);  //Service.class相当于强转了
        service.save();
    }
}

1.3实例工厂

factroy.class

public  class ServiceFactory {
    public Service createService(){
        return new ServiceImpl();
    }
}

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.xsd">

        <bean id="serviceFactory" class="com.test.ServiceFactory"></bean>
        <bean id="userService" factory-bean="serviceFactory" factory-method="createService"></bean> //指定工厂和方法
</beans>

测试

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
        service.save();
    }
}

2、作用域

用于确定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.xsd">
        
        <bean id="userService" class="com.test.ServiceImpl" scope="singleton"></bean>  //singleton单例默认,只存在一个bean,(默认),prototype:多例
</beans>

测试

public class Test{
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
        ServiceImpl service2 = applicationContext.getBean("userService",ServiceImpl.class);
        System.out.println(service);
        System.out.println(service2);
    }
} 

3、生命周期

3.1初始化和销毁

目标bean

public class ServiceImpl implements Service {
    public void save(){
        System.out.println("save");
    }
    public void init(){
        System.out.println("初始化");
    }
    public void destroy(){
        System.out.println("销毁");
    }
}

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.xsd">

        <bean id="userService" class="com.test.ServiceImpl" init-method="init" destroy-method="destroy"></bean> //配值初始化和销毁方法
</beans>

测试

public class Test{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
        System.out.println(service);
        applicationContext.getClass().getMethod("close").invoke(applicationContext); //当容器关闭了,才会调用destory方法

        //如果不用反射
        //ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        //ServiceImpl service = applicationContext.getBean("userService",ServiceImpl.class);
        //System.out.println(service);
        //applicationContext.close();
    }
}  

要求:该bean需要是单例模式和容器需要调用close方法,destory方法才会被执行。

3.2BeanPostProcessor(后处理bean)

容器中所有的 bean 被初始化前后会调用者两个方法,如果有多个接口实现了BeanPostProcessor,这些接口中的方法都会被执行

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before:"+beanName);
        return bean;
    }
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("after:"+beanName);
        return Proxy.newProxyInstance( //如果需要对执行的方法进行特殊的处理,例如写入日志等等。如果没有特别需求,可以直接 return bean;
                MyBeanPostProcessor.class.getClassLoader(),
                bean.getClass().getInterfaces(),
                new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("开启事务");
                //执行目标代码
                Object invoke = method.invoke(bean, args);
                System.out.println("结束事务");
                return invoke;
            }
        });
    }
}

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.xsd">

        <bean id="userService" class="com.test.ServiceImpl" init-method="init" destroy-method="destroy"></bean> //查看之前写的ServiceImpl
        <bean class="com.test.MyBeanPostProcessor"></bean> //只需要添加一个class就可以监听bean的初始化了
</beans>

测试

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationTargetException;

public class Test{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        Service service = applicationContext.getBean("userService",Service.class);  //Service.class,不能是ServiceImpl.class
        service.save();
        applicationContext.getClass().getMethod("close").invoke(applicationContext); //当容器关闭了,才会调用destory方法
    }
}

3.3属性依赖注入

3.3.1构造方法注入

public class User {
    private Integer uid;
    private String username;
    private Integer age;

    public User(Integer uid, String username) {
        super();
        this.uid = uid;
        this.username = username;
    }
    public User(String username, Integer age) {
        super();
        this.username = username;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "uid=" + uid +
                ", username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

xml

<bean id="user" class="com.test.User">
        <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
        <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>

测试

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);

3.3.2setter方法注入

public class Person {
    public String name;
    public Address addr;

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", addr=" + addr +
                '}';
    }
}

 

public class Address {
    private String addr;

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Address{" +
                "addr='" + addr + '\'' +
                '}';
    }
}

xml

    <bean id="user" class="com.test.User">
        <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
        <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
    </bean>

    <bean id="person" class="com.test.Person">
        <property name="name" value="小明"></property>
<!--        <property name="name">-->
<!--            <value>小明</value>-->
<!--        </property>-->
        <property name="addr" ref="homeAddrId"></property>
<!--        <property name="addr">-->
<!--            <ref bean="homeAddrId"/>-->
<!--        </property>-->
    </bean>
    <bean id="homeAddrId" class="com.test.Address">
        <property name="addr" value="北京"></property>
    </bean>

补充

  SpEL[了解]

  对<property>进行统一编程,所有的内容都使用value

  格式:<property name="" value="#{表达式}">

    #{123}、#{'jack'} : 数字、字符串

    #{beanId} :另一个bean引用

    #{beanId.propName} :操作数据

    #{beanId.toString()} :执行方法

    #{T(类).字段|方法} :静态方法或字段

    <!-- 
        <property name="cname" value="#{'jack'}"></property>
        <property name="cname" value="#{customerId.cname.toUpperCase()}"></property> 通过另一个bean,获得属性,调用的方法 
        <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property> ?. 如果对象不为null,将调用方法 
    -->
    <bean id="customerId" class="com.itheima.f_xml.d_spel.Customer">
        <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>  //?:表示如果是空,就不转换大写
        <property name="pi" value="#{T(java.lang.Math).PI}"></property>
    </bean>

 3.3.3 集合注入

public class CollData {
    private String[] array;
    private List<Integer> list;
    private Set<String> set;
    private Map<String,String> map;
    private Properties properties;

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setList(List<Integer> list) {
        this.list = list;
    }

    public void setArray(String[] array) {
        this.array = array;
    }

    @Override
    public String toString() {
        return "CollData{" +
                "array=" + Arrays.toString(array) +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

xml

<bean id="collData" class="com.test.CollData">
        <property name="array">
            <array>
                <value>sdf</value>
                <value>sdf</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>sdf</value>
                <value>sdf</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="name" value="小明"></entry>
                <entry>
                    <key><value>age</value></key>
                    <value>10</value>
                </entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="name">小明</prop>
                <prop key="age">10</prop>
            </props>
        </property>
    </bean>

  

三、基于注解装配Bean

在之前

需要生成一个spring-context.xml,来配置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"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="springContext" class="com.zy.myshop.comment.until.MyApplicationContext" />

        <bean id="getuserImpl" class="com.zy.myshop.dao.impl.GetUserImpl"></bean>

        <bean id="userLoginimpl" class="com.zy.myshop.service.impl.UserLoginImpl"></bean>
</beans>

  

 

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 Bean 实例。Spring 中使用注解, 需要在原有 Spring 运行环境基础上再做一些改变

需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解

<?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">

        //<bean id="springContext" class="com.zy.myshop.comment.until.MyApplicationContext" />  

        <context:annotation-config />                            //xml和注解混和使用,就是如果bean用xml配置,依赖注入使用注解的话,如果不加上这句,会出现注解不生效
        <context:component-scan base-package="com.zy.myshop"/>   //组件扫描,扫描含有注解的类
</beans>

说明:schema命名空间

命名空间声明

  默认:xmlns=""

  显示:xmlns:别名="",例如xmlns:context

确定schema xsd 文件位置

  xsi:schemaLocation="名称 位置 名称2 位置2......."(成对)

 

3.1 注解(解放xml装配bean):

@Component:      需要在类上使用注解 @Component,该注解的 value 属性用于指定该 bean 的 id 值。(像一般工具类等的注解)

  取代<bean class=""> @Compose("id") 取代<bean id="" class="">

下面是web开发@component衍生注解(功能一样)

@Repository:    用于对 DAO 实现类进行注解    (数据访问层)

@Service       用于对 Service 实现类进行注解    (逻辑业务层)

@Controller:               用于对 Controller 实现类进行注解 (视图层)

@RestController:           用于对 Controller 实现类进行注解,不再返回视图,全部必须返回json数据不需要再加上@ResponseBody注解(表示返回的数据为json格式)

 

3.2 依赖注入(给bean添加属性)

3.2.1 普通值

  @Value

需要在属性上使用注解 @Value,该注解的 value 属性用于指定要注入的值。

 

  application.xml配置,需要扫描到配置文件

<context:property-placeholder location="classpath:sms.properties"></context:property-placeholder>

3.2.2 引用值

方式1:按类型注入

  @Autowired

前提:一个接口只有一个实现类

需要在域属性上使用注解 @Autowired,该注解默认使用 按类型自动装配 Bean 的方式。

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

 

方式2:按名称注入

  @Autowired
  @Qualifier("名称")

方式3:按名称注入

  @Resource("名称")

需要在域属性上使用注解 @Resource,该注解有一个 name 属性,可以创建指定的 bean

@Resource(name = "userService")
private UserService userService;

3.2.3作用域

  @Scope

需要在类上使用注解 @Scope,其 value 属性用于指定作用域。默认为 singleton。

3.2.4初始化和销毁

  方式1:@PostConstruct

在方法上使用 @PostConstruct 相当于初始化

@PostConstruct
public void myInit(){
    System.out.println("初始化");
}
@PreDestroy
public void myDestory(){
    System.out.println("销毁");
} 

  方式二:xml配置(略)

  方式三:@Bean注解,指定初始化方法和销毁方法

//指定User对象初始化和销毁方法    
@Bean(destroyMethod = "",initMethod = "")
public User user(){
        
}

 

3.3基本使用(示例)

注意此时有一个spring-aop包很重要,maven中依赖的sprint-context包包含了spring-aop包

dao

public interface StudentDao {
    void save();
}

daoImpl

@Repository("studentDao")
public class StudentDaoImpl implements StudentDao {
    @Override
    public void save() {
        System.out.println("保存数据");
    }
}

service

public interface StudentService {
    public void save();
}

serviceImpl

@Service
public class StudentServiceImpl implements StudentService{

    @Autowired
    @Qualifier(value = "studentDao") //按名字注入(Dao必须指定名字)
    //@Resource(name = "studentDao") //这一句可以代替上面两句
    private StudentDao dao;

    @Override
    public void save() {
        System.out.println("调用dao");
        dao.save();
    }
}

controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


@Controller(value = "studentController")
public class StudentController{

    @Autowired //按照类型注入
    private StudentService service;

    public void save(){
        System.out.println("调用service");
        service.save();
    }

    @PostConstruct
    public void myInit(){
        System.out.println("初始化");
    }
    @PreDestroy
    public void myDestory(){
        System.out.println("销毁");
    }
}

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.xsd">

    <context:component-scan base-package="com.test"></context:component-scan>
</beans>

测试

public class Test{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        StudentController StudentController = ((StudentController) applicationContext.getBean("studentController"));
        StudentController.save();

        applicationContext.getClass().getMethod("close").invoke(applicationContext);
    }
}

  

3.4 补充

MyapplicationContext

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;


//public class MyApplicationContext {
//    public static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");;
//}

public class MyApplicationContext implements ApplicationContextAware, DisposableBean {
    public static final Logger logger = LoggerFactory.getLogger(MyApplicationContext.class);
    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    public void destroy() throws Exception {
        logger.debug("清空 applicationContext");
        System.out.println("清空application.....");
        applicationContext = null;

    }


    public static <T> T getBean(String name) {
        assertContextInjected();
        logger.info("getbean");
        System.out.println("getBean........");
        return (T) applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
        assertContextInjected();
        return applicationContext.getBean(clazz);
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        logger.info("加载 application");
        System.out.println("加载application");
        MyApplicationContext.applicationContext = applicationContext;
    }

    private static void assertContextInjected() {
        Validate.validState(applicationContext != null, "applicationContext 属性未注入,请在 spring-context.xml 配置中定义 SpringContext");
    }
}

  

 

在托管的类上使用注解

 

 

 

注解与 XML 配置的区别

注解的好处是,配置方便,直观。但其弊端也显而易见:以硬编码的方式写入到了 Java 代码中,其修改是需要重新编译代码的。

XML 配置方式的最大好处是,对其所做修改,无需编译代码,只需重启服务器即可将新的配置加载。

若注解与 XML 同用,XML 的优先级要高于注解。这样做的好处是,需要对某个 Bean 做修改,只需修改配置文件即可。

 

 

posted @ 2019-05-08 10:59  小名的同学  阅读(467)  评论(0编辑  收藏  举报