Spring之Ioc以及注解Annotation

1.1.Spring 概述
1.Spring 是什么?(框架,半成品)
2.Spring 能解决什么问题(面向对象,面向切面,面向服务)
3.Spring 框架核心?(IOC,AOP,MVC,…)

个人认为:Spring 最强大是它的资源整合能力。

1.2.Spring IOC 概述
1.IOC是什么?(控制反转:由spring构建对象,管理对象依赖)
2.IOC 应用优势?(解耦,更好的管理对象,使用系统资源)
3.IOC 的核心?(工厂,配置,依赖注入)
4.IOC 编程步骤?(类,配置,获取)

1.3.Spring IOC 编程
1.Spring Bean对象初始化,作用域,声明周期,延迟加载
1)Bean类型的编写(修饰符,构造方法)
2)Bean 的配置(applicationContext.xml)
3)Bean 的作用域(singleton,prototype)
4)Bean 的生命周期(生命周期方法的使用)
5)Bean 的延迟加载(局部lazy-init,全局 default-lazy-init)

2.Spring Bean依赖(依赖注入,自动装配)
1)依赖注入(DI)的定义(通过spring为类中的属性注入值)
2)依赖注入的实现(set注入,构造注入)
3)依赖注入中的集合值的注入(数组,list,set,map)
4)依赖注入中的自动装配(未讲)

2.Spring Bean依赖
2.1.1.依赖注入基础
重点了解set,构造注入的方式以及单个值如何实现注入。
2.1.2.依赖注入进阶
重点了解集合类型值的注入,例如list,hashmap,properties等
案例1:

//定义一个相对复杂的对象
public class ComplexObject {
    private String[] names;
    private List<String> address;
    private Map<String,Integer> map;
    private Properties properties;
//set,get
}
在配置文件中配置此对象,并为属性注入值

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 

      <bean id="cObj" class="beans.ComplexObject">
         <!-- 为String[]数组属性注入值 -->
         <property name="names">
             <list>
                <!-- #{}为spring中的一个表达式 -->
                <value>name-1</value>
                <value>name-2</value>
             </list>
         </property>
         <!-- 为list<String>集合注入值 -->
         <property name="address">
             <list>
               <value>北京</value>
               <value>深圳</value>
               <value>上海</value>
             </list>
         </property>
         <!-- 为map属性注入值 -->
         <property name="map">
            <map>
               <entry key="k1" value="200"/>
               <entry key="k2" value="300"/>
            </map>
         </property>
         <!-- 为properties属性注入值 -->
         <property name="properties">
            <props>
              <prop key="pk1">pv1</prop>
              <prop key="pk2">pv2</prop>
            </props>
         </property>

      </bean>
</beans>

案例2:spring配置文件中引入properties文件中的数据

step1:类路径的根目录定义properties文件cfg.properties,其内容如下:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=root

step2:在spring配置文件中引入此配置文件(借助util标签)

<util:properties 
          id="cfg" 
          location="classpath:cfg.properties"/>

step3:在Bean对象中引入配置文件中的值

<bean id="dataSource" class="beans.DataSource">
          <property name="driver" value="#{cfg.driver}"/>
          <property name="url"  value="#{cfg.url}"/>
          <property name="username" value="#{cfg.username}"/>
          <property name="password" value="#{cfg.password}"/>
       </bean>

**其中#{}为spring中一个表达式,通过这个表达式可以获取properties文件中的值
例如#{cfg.driver}为获取id为cfg对应的对象中key为driver的值。**

2.1.3.依赖值的自动装配

Spring中为bean对象中的属性提供了自动装配功能,此功能的开启需要借助bean标签中的autowire属性进行指定,此属性的值默认有如下几个:

  1. NO 自动配置(默认)
  2. ByName 按名字自动装配(重点掌握)(方法名字)
  3. ByType 按类型自动装配(重点掌握),但有多个参数类型构造方法时会出错(参数类型)
  4. Constructor 与byType类似,不同之处在于它应用于构造器参数。

例如:

定义并配置DataSource对象

public class DataSource {
}


<bean id="dataSource" class="beans.DataSource"/>


定义并配置JdbcTemplate对象

public class JdbcTemplate {
    private DataSource dataSource;
    public JdbcTemplate() {}
    public JdbcTemplate(DataSource dataSource) {
        this.dataSource=dataSource;
    }
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

   <bean id="jdbcTemplate"
          class="beans.JdbcTemplate"
          autowire="constructor">
   </bean>

自动装配应用总结

byName 首先根据配置文件里的所有bean对象id去类里面找名字相同的方法,(首字母大小写不管,底层自动换成小写)按属性对应的set方法名从容器中查找名字相同的bean,然后进行注入。假如出现了名字相同,但类型不同的Bean对象时,会注入失败
byType 先从容器中查找属性类型相匹配的值然后找按类中对应的set方法(看参数类型),最后通过set方法为对象的属性注入值。假如容器中出现多个类型相同的对象,就会注入失败。
constructor 先从容器中查找属性类型相匹配的值,然后找类中对应的构造方法(看参数类型)最后通过构造方法为对象的属性注入值。假如容器中出现多个类型相同的对象,此时再比对参数名字,假如有同名的则直接注入,没有同名的就会注入失败。
3. Spring 注解应用
3.1.Spring 注解概述
Spring中提供了两种方式对Bean进行描述,一种是基于xml方式,一种是基于注解方式。基于注解方式主要是依托于注解对bean以及bean中的属性进行描述
然后spring底层通过反射获取bean上定义的这些注解,通过注解描述初始化对象,管理对象的作用域以及对象与对象之间的依赖。

3.2.Spring 注解基本应用

3.2.1.常用注解说明
组件应用注解
注解名 说明

1.  @Component  通用注解
2.  @Repository 持久层组件应用注解
3.  @Service    业务层组件应用注解
4.  @Controller 控制层组件应用注解

3.2.2.注解应用案例

数据层对象
@Repository
public class SysUserDaoImpl implements SysUserDao{
    public void saveUser(Object obj) {
        System.out.println("dao.save.user");
    }
}

业务层对象
@Service
public class SysUserServiceImpl implements SysUserService{
…
}

控制层对象
@Controller
public class UserController {

}

3.2.3.配置注解扫描

Spring中通过指定一个包路径,由系统自动扫描该包及其子包所有组件类,当发现组件类定义前有特定的注解标记时,就将该组件纳入到Spring容器。

例如:
使用组件扫描,首先需要在XML配置中指定扫描父级package路径,例如

<context:component-scan
 base-package=”com.company.spring/>”

在这个配置中,容器会自动扫描org.example包及其子包下所有组件,并实例化bean对象。

3.2.4.编写测试类获取bean

public class TestSysUserDao {
    private ClassPathXmlApplicationContext ctx;
    @Before
    public void init(){
        ctx=new ClassPathXmlApplicationContext("ioc.xml");
    }
    @Test
     public void testSysUserDao() {
        SysUserDao sys = ctx.getBean("sysUserDao",SysUserDao.class);
    sys.insertObject();
    }
    @After
    public void destroy(){
        ctx.close();

3.3.Spring 注解应用增强

3.3.1.作用域及生命周期

@Scope("singleton")
@Repository
public class SysUserDao {
      /**@PostConstruct注解修饰方法在对象初始化时执行*/
      @PostConstruct
      public void init(){
        System.out.println("init");  
      }
      public void insertObject(){
          System.out.println("insertObject");
      }
      /**@PreDestroy对象销毁时执行*/
      @PreDestroy
      public void destory(){
          System.out.println("destory");
      }
}

3.3.2.延迟加载配置

@Lazy(false)
@Service
public class SysUserService {
}

3.3.3.自动装配配置

注解方式的自动装配一般会借助@Autowired和@Resource实现,具体过程参考
课堂案例,然后总结实践其过程(作业)。

/**使用@Service修饰的对象一般为一个业务层对象
 * @Lazy 注解用于定义这个类是否要延迟加载,
 * true表示延迟加载,默认为true
 * */
@Lazy(false)
@Service
public class SysUserService {
    /**@Autowired 注解用于修饰属性或者set方法
     * 表示默认按属性类型或对应的set方法参数类型
     * 进行自动装配,假如spring容器中有多个类型
     * 相同bean对象,此时还会名字进行匹配,假如
     * 名字也没有相同的则匹配失败*/
    @Autowired
    @Qualifier("sysUserDao")
    private SysUserDao sysUserDao;
    public void setSysUserDao(SysUserDao sysUserDao) {
        this.sysUserDao = sysUserDao;
    }
    public SysUserService() {
        System.out.println("SysUserService()");
    }

    public void saveObject(){
        //log..
        sysUserDao.insertObject();
        //log..

4.Spring 注解工厂应用剖析

4.1.Bean工厂是用于做什么的?

所有的工厂都是用于创建对象的,对象创建一般会基于某种策略
对对象进行存储。

例如Spring 中的Bean工厂:ApplicationContext
1)ClassPathXmlApplicationContext  基于mxl
2)AnnotationApplicationContext    基于注解
3)…….

4.2.Bean工厂的实现的基本原理?

Spring 中所有的Bean假如需要由Spring管理,此时需要以xml描述的方式或
注解的描述方式告诉spring容器。Spring底层内置了对xml的解析,通过反射
获取注解描述,然后基于这些描述通过反射构建对象。

4.3.Bean工厂理解的拔高?
手写基于注解方式的Spring Bean工厂

1)包的扫描(将包转换为类所在的路径,然后进行文件的查找)
2)构建类全名(包名+类名,例如java.util.Date)
3)基于反射构建类的对象(获取Class对象,构建构造方法对象,构建类的实例对象)
4)存储类的对象(key是谁)
5)需要时从工厂中获取bean对象即可

4.4.Bean 工厂手写应用实践

4.4.1.注解定义实现

定义一个用于修饰bean,描述bean的注解名字为Component。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {     
    String value() default "";
}

定义一个用于描述配置的注解名字为ComponentScan。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
      String value() default "";
}

4.4.2.定义Bean组件
定义一个IdGenerator类,然后使用@Component注解修饰

@Component
public class IdGenerator {}

4.4.3.定义Bean工厂
这个Bean工厂要实现的功能是基于配置类上的@ComponentScan注解,对注解中定义包进行类的扫描,然后构建类的对象,再将对象存储到map集合,需要时从map获取。其代码如下:

public class AnnotationAppContext{
    private Map<String,Object> beanMap=
    new HashMap<String,Object>();
    public AnnotationAppContext(Class<?> c)throws Exception{
        //1.获取class(例如AppConfig)上的@ComponentScan注解
        ComponentScan cs=
        c.getAnnotation(ComponentScan.class);
        //2.获取注解中定义的包名
        String pkg=cs.value();
        //替换包名中的“.”,改成目录结构
        String dir=pkg.replaceAll("\\.", "/");
        //3.基于包获取包对应的类路径getClass为得到当前运行的类。getClassLoader为得到类加载器,返回类加载器。getResource()查找具有给定资源的路径。
        URL url=
        getClass().getClassLoader()
        .getResource(dir);
        System.out.println("url="+url);
        //4.得到路径指定的文件使其变成文件的形式(例如IdGenerator)
        File pkgDir=new File(url.getPath());
        //4.1获取目录下所有的文件
        File[] fs=pkgDir.listFiles();//class
        //4.2遍历所有文件,构建类全名
        for(File f:fs){
            String fname=
            f.getName().substring(0,f.getName().lastIndexOf("."));
            String clsName=pkg+"."+fname;
            Class<?> cls=Class.forName(clsName);
            //5.基于class文件上注解(@Component)描述构建类对象
 //isAnnotationPresent(Component.class)如果指定的类型注释存在此元素上,返回true
            if(!cls.isAnnotationPresent(Component.class))
            {continue;}
            Object obj=newInstance(cls);
            //6.将类对象存储到map集合
            //6.1获取key的值
            Component cp=cls.getAnnotation(Component.class);
            //获取包名
            String key=cp.value();
            //如果对注解没有命名,则设置为默认类名首字母小写
            if("".equals(key)){
                key=String.valueOf(
                fname.charAt(0)).toLowerCase()+
                fname.substring(1);
            }
            //6.2存储到map
            beanMap.put(key, obj);
        }
    }
     //Object obj=newInstance(cls)的方法
    private Object newInstance(Class<?> cls)
    throws Exception{
        Constructor<?> c=
        cls.getDeclaredConstructor();
        c.setAccessible(true);
        return c.newInstance();
    }
    public Object getBean(String beanName){
        return beanMap.get(beanName);
    }
    @SuppressWarnings("unchecked")
    public <T>T getBean(String beanName,
            Class<T> cls){
        return (T) beanMap.get(beanName);
    }
    public static void main(String[] args)
    throws Exception{
        AnnotationAppContext ctx=
        new AnnotationAppContext(AppConfig.class);

        IdGenerator obj1=(IdGenerator)
        ctx.getBean("idGenerator");
        IdGenerator obj2=
        ctx.getBean("idGenerator",IdGenerator.class);

        System.out.println(obj1);
        System.out.println(obj2==obj1);
    }
}//两个是同一个对象

猜你喜欢

转载自blog.csdn.net/TomHaveNoCat/article/details/82712167