spring初学

Posted 蜗牛丶sky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring初学相关的知识,希望对你有一定的参考价值。

一、spring概述

1.1 什么是spring

  spring就是把每个bean(实体类)与bean的关系全部交给第三方容器进行管理,这个容器就是spring,管理bean的生命周期。且可以和其他框架一起使用;Spring与Struts,  Spring与hibernate(起到整合(粘合)作用的一个框架)。

1.2 spring提供了一站式解决方案:

  1) Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系。

  2) Spring Web  Spring对web模块的支持。【可以与struts整合,让struts的action创建交给spring】;【spring mvc模式】

  3) Spring DAO  Spring 对jdbc操作的支持【JdbcTemplate模板工具类】

  4) Spring ORM  spring对orm的支持。既可以与hibernate整合,【session】;也可以使用spring的对hibernate操作的封装

  5)Spring AOP  切面编程

  6)SpringEE   spring 对javaEE其他模块的支持

二、spring的环境搭建

2.1 pom.xml文件引入maven坐标

<!-- 引入Spring-AOP等相关Jar -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.3</version>
        </dependency>
<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_2</version>
        </dependency>

2.2 需要交给spring管理的bean类

public class UserEntity {
    private String name;
    private Integer age;
    //set / get 
}

2.3 spring配置文件

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">    
<beanid="userEntity"class="com.stu.entity.UserEntity"/>
</beans>

2.4 测试类

public class SpringTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserEntity userEntity = (UserEntity) applicationContext.getBean("userEntity");
        System.out.println(userEntity);
    }
}

三、spring的加载过程

3.1 spring是单例还是多例

  spring默认单例的,可通过构造函数证明,也可多次获取该对象比较内存地址。线程不安全,使用共享数据注意安全问题(尽量少使用共享数据)。

3.2 spring的作用域

  3.2.1 singleton作用域

    当一个bean的 作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean。

  3.2.2 prototype作用域

    prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)

  总结:spring的生命周期:<bean id="userEntity" class="com.stu.entity.UserEntity" scope="prototype"></bean>

    在这个spring中 有四种生命周期:
    prototype 多例 当你调用getbean方法才会初始化
    singleton 单例,加载spring时实例化 
    request http请求request作用里面使用
    session http请求session作用里面使用

四、SpringIOC容器

  SpringIOC容器,是spring核心内容。作用:创建对象&处理对象的依赖关系

  SpringIOC原理:解析xml配置,获取配置的bean的class地址,通过反射实例化,返回对象。

4.1 创建对象

  1)调用无参数构造器 默认

  2)带参数构造器

  3)工厂创建对象:工厂类,静态方法创建对象;工厂类,非静态方法创建对象

<!-- 无参构造函数 -->
    <beanid="user1"class="com.stu.entity.UserEntity"scope="prototype"/>
    <!-- 有参构造函数 -->
    <beanid="user2"class="com.stu.entity.UserEntity">
        <constructor-argname="name"type="java.lang.String" value="张三"></constructor-arg>
        <constructor-argname="age"type="java.lang.Integer" value="18"></constructor-arg>
    </bean>

    <beanid="factory"class="com.stu.entity.ObjectFactory"></bean>
    <!-- 通过实例工厂方法创建 -->
    <beanid="user3"factory-bean="factory"factory-method="getInstance"></bean>
    <!-- 通过静态工厂方法创建 -->
    <beanid="user4"class="com.stu.entity.ObjectFactory" factory-method="getStaticInstance"></bean>

4.2 依赖注入

  1) 通过构造函数

  2) 通过set方法给属性注入值

  3) p名称空间

  4) 注解

  #Set方法注入值

<!-- dao instance -->
    <beanid="userDao" class="cn.stu.UserDao"></bean>

    <!-- service instance -->
    <beanid="userService" class="cn.stu.UserService">
        <property name="userDao"ref="userDao"></property>
    </bean>
    
    <!-- action instance -->
    <beanid="userAction" class="cn.stu.UserAction">
        <property name="userService" ref="userService"></property>
    </bean>

  使用set方法注入,必须要有setXXX方法。因为是通过反射原理来实现的。

  #p名称空间注入属性值

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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">
    
    <!-- ###############对象属性赋值############### -->
    
    <!-- 
        给对象属性注入值:
            # p 名称空间给对象的属性注入值
             (spring3.0以上版本才支持)
     -->
    <beanid="userDao" class="cn.stu.UserDao"></bean>
    <beanid="userService" class="cn.stu.UserService" p:userDao-ref="userDao"></bean>
    <beanid="userAction" class="cn.stu.UserAction" p:userService-ref="userService"></bean>
      
    <!-- 传统的注入:
    <bean id="user" class="cn.itmayiedu.User" >
        <property name="name" value="xxx"></property>
    </bean>
    -->
    <!-- p名称空间优化后 -->
    <beanid="user" class="cn.stu.User" p:name="Jack0001"></bean>
</beans>

  #注解版本使用

  @Repository    //标识注解dao层,默认以 <bean id=”userDao” class=”com.stu.dao.UserDao”></bean>

  @Service 注解service层,以<bean id=”userService” class=”com.stu.service.UserService”></bean>

  使用注解步骤:

    1)先引入context名称空间

      xmlns:context="http://www.springframework.org/schema/context"

    2)开启注解扫描

      <context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan>

    3)使用注解

      通过注解的方式,把对象加入ioc容器。

  创建对象以及处理对象依赖关系,相关的注解:

    @Component 指定把一个对象加入IOC容器

    @Repository 作用同@Component;在持久层使用

    @Service 作用同@Component;在业务逻辑层使用

    @Controller 作用同@Component;在控制层使用

    @Resource 属性注入    属于java的,jdk1.6,默认以名称查找,再以类型查找

    @Autowired 属性注入  属于spring框架自带,默认以类型查找

  总结:

    1)使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)

    2)注解可以和XML配置一起使用。

  beanID重复:XML冲重复,文件包错;XML和注解重复,优先XML。

五、代理模式

5.1 概述

  代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理访问目标对象。这样好处:可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)

  

  代理模式的关键:代理对象、目标对象

5.2 静态代理

  代理对象,要实现与目标对象一样的接口。

总结静态代理:

  1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。

  2)缺点:

    因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。

    一旦接口增加方法,目标对象与代理对象都要维护。

解决:

         代理工厂?可以使用动态代理。

5.3 动态代理(JDK)

  动态代理:

    1)代理对象,不需要实现接口;

    2)代理对象的生成,是利用JDKAPI,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型);

    3)  动态代理, JDK代理,接口代理

  JDK中生成代理对象的API:

    |-- Proxy

      static Object newProxyInstance(

        ClassLoader loader,  //指定当前目标对象使用类加载器

        Class<?>[] interfaces,   //目标对象实现的接口的类型

        InvocationHandler h  //事件处理器

      ) 

  动态代理总结:

    代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用动态代理!

思考:有一个目标对象,想要功能扩展,但目标对象没有实现接口,怎样功能扩展?

  Class  UserDao{}

    // 子类的方式

    Class subclass  extends  UserDao{}                  

    //以子类的方式实现(cglib代理)

5.4 Cglib代理(ASM)

  Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展

  1)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
  2)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
  3)CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

  Cglib子类代理:

    1) 需要引入cglib – jar文件,但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。

    2)引入功能包后,就可以在内存中动态构建子类

    3)代理的类不能为final,否则报错。

    4)目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

在Spring的AOP编程中,

         如果加入容器的目标对象有实现接口,用JDK代理;

         如果目标对象没有实现接口,用Cglib代。

六、AOP编程

  AOP,aspect object programming,面向切面的编程,AOP可以实现“业务代码”“与“关注点代码”分离。

  关注点:重复代码就叫做关注点;

  切面:关注点形成的类,就叫切面(类)!

    面向切面编程,就是指对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。

  切入点:执行目标对象方法,动态植入切面代码。

    可以通过切入点表达式,指定拦截哪些类的哪些方法;给指定的类在运行的时候植入切面类代码。

  场景:权限、日志、控制、事物原理

// 保存一个用户
public void add(User user) { 
        Session session = null; 
        Transaction trans = null; 
        try { 
            session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】
            trans = session.beginTransaction();    // 【关注点代码】
            
            session.save(user);     // 核心业务代码
            
            trans.commit();     //…【关注点代码】

        } catch (Exception e) { 
            e.printStackTrace(); 
            if(trans != null){ 
                trans.rollback();   //..【关注点代码】

            } 
        } finally{ 
            HibernateSessionFactoryUtils.closeSession(session);   ////..【关注点代码】

        } 
   } 

  分析总结:

    关注点代码,就是指重复执行的代码。

    业务代码与关注点代码分离,好处?

      1)关注点代码写一次即可;

      2)开发者只需要关注核心业务;

      3)运行时期,执行核心业务代码时候动态植入关注点代码;【代理】

  如何分离?

    过程式/对象式/代理模式分离

七、注解方式AOP编程

步骤:

  1)先引入aop相关jar文件          (aspectj  aop优秀组件)                                      

    spring-aop-3.2.5.RELEASE.jar【spring3.2源码】

    aopalliance.jar                                      【spring2.5源码/lib/aopalliance】

    aspectjweaver.jar                      【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\\lib】

    aspectjrt.jar                                 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\\lib】

  注意:用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。

    需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。 

  2) bean.xml中引入aop名称空间  

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

  3)开启aop注解

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

  4) 使用注解

    @Aspect                                                              指定一个类为切面类             

    @Pointcut("execution(* com.itmayiedu.service.UserService.add(..))")指定切入点表达式

    @Before("pointCut_()")                                  前置通知: 目标方法之前执行

    @After("pointCut_()")                                               后置通知:目标方法之后执行(始终执行)

    @AfterReturning("pointCut_()")                   返回后通知:执行方法结束前执行(异常不执行)

    @AfterThrowing("pointCut_()")                    异常通知:  出现异常时候执行

    @Around("pointCut_()")                                 环绕通知:环绕目标方法执行

public class UserAop {
    @Before("execution(* com.stu.service.UserEntityService.addUser(..))")
    public void begin() {
        System.out.println("前置通知");
    }
    @After("execution(* com.stu.service.UserEntityService.addUser(..))")
    public void commit() {
        System.out.println("后置通知");
    }
    @AfterReturning("execution(* com.stu.service.UserEntityService.addUser(..))")
    public void afterReturning() {
        System.out.println("运行通知");
    }
    @AfterThrowing("execution(* com.stu.service.UserEntityService.addUser(..))")
    public void afterThrowing() {
        System.out.println("异常通知");
    }
    @Around("execution(* com.stu.service.UserEntityService.addUser(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("我是环绕通知-前");
        proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知-后");
    }
}

@Service
public class UserEntityService {
    @Autowired
    private UserEntityDao userEntityDao;public void addUser(){
        userEntityDao.addUser();
    }
}

八、XML方式实现AOP编程

  Xml实现aop编程:

    1)引入jar文件【aop 相关jar, 4个】

    2)引入aop名称空间

    3)aop 配置

      * 配置切面类(重复执行代码形成的类)

      * aop配置

        拦截哪些方法 / 拦截到方法后应用通知代码

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- dao实例 -->
    <beanid="userDao" class="com.stu.UserDao"></bean>
    <beanid="orderDao" class="com.stu.OrderDao"></bean>
    
    <!-- 切面类 -->
    <beanid="aop" class="com.stu.Aop"></bean>
    
    <!-- Aop配置 -->
    <aop:config>
        <!-- 定义一个切入点表达式:拦截哪些方法 -->
        <aop:pointcut expression="execution(* com.stu.*.*(..))" id="pt"/>
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"/>
            <!-- 前置通知:在目标方法调用前执行 -->
            <aop:before method="begin" pointcut-ref="pt"/>
            <!-- 后置通知: -->
            <aop:after method="after" pointcut-ref="pt"/>
            <!-- 返回后通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
</beans>

 

以上是关于spring初学的主要内容,如果未能解决你的问题,请参考以下文章

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

Spring boot:thymeleaf 没有正确渲染片段

What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

spring初学