Spring小结

Spring

IOC

一. 简介
  • 什么是Spring
    spring是分层的Java SE/EE应用 full-stack(服务端全栈) 轻量级开源框架,以IOCAOP为内核

    • IOC : Inversion of Controll控制反转,用于解耦
    • AOP: Aspect oriented Programming,面向切面编程,本质是动态代理,不修改源码的情况下进行功能增强
    • spring 提供了
      • 表示层 Spring MVC
      • 持久层 Spring JDBCTemplate, Spring Data
      • 业务层事务管理等
    • 能整合开源世界众多著名的第三方框架和库类,逐渐成为使用最多的Java EE企业应用开源框架
  • Spring的优势

    • 方便解耦,简化开发
    • AOP编程的支持
    • 声明事务的支持
    • 方便程序的测试(集成了Junit)
    • 方便集成各种优秀框架(SSM集成)
    • 降低了Java EE API的使用难度
二. 工厂模式解耦
  • 耦合性问题
    • 耦合性:程序之间的依赖性
      - 编译期以来:变异是必须提供依赖的类,否则变异不通过.应避免编译期依赖
      - 运行期依赖:运行时必须提供依赖的类,否则不能运行
    • 耦合性越强,维护成本越高
    • 开发时要求::高内聚,低耦合
  • 解耦的思路
    • 可以使用反射技术,代替new创建对象 ,避免编译期依赖
      Class clazz = Class.forName("全限定类名");
      Object obj = clazz.newInstance();

    • 再把全限定类名提取到配置文件中(xml或properties),读取配置文件信息来反射创建对象
      //读取配置文件,得到要创建对象的全限定类名;再通过反射技术创建对象
      String className = ...;
      Class clazz = Class.forName(className);
      Object obj = clazz.newInstance();

    • 使用工厂模式解耦
      - UserService 提供用户相关的功能,需要调用UserDao完成数据库操作
      - 使用工厂模式 + 配置文件的方式,降低他们之间的耦合性

      • 示例
-  创建项目,导入依赖 
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

- dao层代码
public interface UserDao {
    void save();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl.save......");
    }
}
- service层代码
public interface UserService {
    void save();
}

public class UserServiceImpl implements UserService {

    /**
     * 存在编译期依赖:如果没有UserDaoImpl,代码编译是不通过的。
     * 要避免编译期依赖,减少运行期依赖
     * 解决思路:
     * 1. 使用反射技术代替new
     * 2. 提取配置文件
     */
    //private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = (UserDao) BeanFactory.getBean("userDao");

    @Override
    public void save() {
        userDao.save();
    }
}

- 配置文件 **bean.properties**
userDao=com.user.dao.impl.UserDaoImpl

- 工厂类**BeanFactory**
public class BeanFactory {

    private static Map<String, Object> map = new HashMap<>();

    static {
        //类加载时,读取properties文件,把其中所有的bean都创建对象,放到一个容器里
        //当需要使用时,直接从容器中获取即可

        try {
            //1.读取配置文件
            ResourceBundle bundle = ResourceBundle.getBundle("beans");
            Enumeration<String> keys = bundle.getKeys();
            while (keys.hasMoreElements()) {
                String id = keys.nextElement();
                String className = bundle.getString(id);
                Class clazz = Class.forName(className);
                Object object = clazz.newInstance();

                map.put(id, object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object getBean(String id){

        return map.get(id);
    }
}
三. 控制反转 IOC
  • 代码演示
1. 创建Maven项目,导入依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

2. 编写UserDao和UserDaoImpl:自己的业务功能代码,无论是否使用框架,都必须要自己编写
3. 提供配置文件★:
   - 名称习惯上叫applicationContext.xml
   - 要把类交给Spring进行管理
       <bean id="唯一标识" class="全限定类名"></bean>
4. 功能测试
       //1. 创建Spring的容器
       ApplicationContext app = 
           new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
       
       //2. 从Spring容器里获取bean对象
       Object obj = app.getBean("bean的id");
   

  • IOC 作用以及使用
    • IoC:控制反转,反转的是创建对象的控制权:需要什么对象,由原来自己主动new,变成从Spring容器里被动接收
    • IoC作用:用于解耦(不存在编译期依赖了)
    • IoC的关键在于配置:告诉Spring,我们想要什么样的bean对象
      • bean标签:声明一个类,要交给Spring进行管理
      • bean标签的配置:声明给Spring,我们要一个什么样的bean对象
        • id:声明bean的唯一标识
        • name:声明bean的名称(少用)
        • class:声明bean的全限定类名,告诉Spring我们要一个什么样的类的对象
        • scope:声明bean的作用范围,告诉Spring我们要单例和还是多例的或者其它的
          • singleton:单例的。一个Spring容器里,只有一个该bean的对象
            • 何时创建:Spring容器初始化时
            • 何时销毁:Spring容器关闭时
          • prototype:多例的。一个Spring,可以生成多个该bean的对象
            • 何时创建:获取bean对象时
            • 何时销毁:长时间不用,JVM会垃圾回收(Spring只管创建,之后交给JVM管理了)
        • init-method:声明一个bean对象的初始化方法。让Spring在创建bean对象之后执行一次
        • destroy-method:声明一个bean对象的销毁方法。让Spring在销毁bean对象之前执行一次
    • bean实例化的常用的三种方式:告诉Spring用什么样方式,可以生成指定的bean对象
      • 默认无参构造方式:使用bean标签
      • id:bean对象的唯一标识
      • class:要生成的bean对象的全限定类名
    • 工厂类的静态方法(静态工厂方式):使用bean标签
      • id:bean对象的唯一标识
      • class:工厂类的全限定类名
      • factory-method:工厂类里,能够生成bean对象的静态方法
    • 工厂对象的非静态方法(实例化工厂方式):使用bean标签
      • id:bean对象的唯一标识
      • factory-bean:能够生成bean对象的那个工厂对象
      • factory-method:工厂对象里,能够生成bean对象的非静态方法
依赖注入
一. 代码演示
1. 创建Maven项目,导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2. 编写dao层和service层代码

- dao层接口UserDao

    public interface UserDao {
        void save();
    }

- dao层实现类UserDaoImpl

    public class UserDaoImpl implements UserDao {
        @Override
        public void save() {
            System.out.println("UserDaoImpl.save......");
        }
    }

- service层接口UserService

    public interface UserService {
        void save();
    }

- service层实现类UserServiceImpl

    public class UserServiceImpl implements UserService {
        //依赖于dao层的UserDao,定义一个成员变量
        private UserDao userDao;
    
        @Override
        public void save() {
            userDao.save();
        }
        
        //提供userDao的get/set方法
        public UserDao getUserDao() {
            return userDao;
        }
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    }

3. 创建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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--配置UserDao-->
        <bean id="userDao" class="com.user.dao.impl.UserDaoImpl"></bean>
        <!--配置UserService-->
        <bean id="userService" class="com.user.service.impl.UserServiceImpl">
            <!--把userDao注入给userService的属性-->
            <property name="userDao" ref="userDao"/>
        </bean>
    </beans>

4. 使用Spring的API,测试

    public class UserTest {
        public static void main(String[] args) {
            ApplicationContext context = 
                new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            userService.save();
        }
    }
  • 注意
<bean id="" class="">
 <property name="userDao" ref="要注入的bean对象"/>
 <property name="属性名称" value="要注入的简单值"/>
</bean>
二. 常见的三种注入方式
  • 如果一个bean对象里,需要其它的数据(有一些依赖项),可以使用Spring注入依赖的数据
  • 常用的三种注入方式:
    • set方法注入:灵活
      • 好处:可以自主决定要注入哪个依赖项的数据
      • 缺点:如果某个依赖项是必须的,用这个方式,就算忘记注入了,也不报错,但是运行会失败
      • bean类里依赖项,要提供set方法
      • 在配置文件里,bean标签内使用property标签注入依赖的数据
       <bean id="" class="">
       	<property name="属性名" value="要注入的简单值"/>
           <property name="属性名" ref="要注入的bean对象"/>
       </bean>
      
    • 构造方法注入:更严谨
      • 好处:如果某个依赖项是必须的,而忘记注入了,编译会报错
      • 坏处:如果一些依赖项是非必须的,也必须要注入
      • bean类里依赖项,要提供有参构造。构造方法里每个参数就是一个依赖项
      • 在配置文件里,bean标签内部使用constructor-arg标签注入依赖的数据
       <bean id="" class="">
       	<constructr-arg name="属性名" value="要注入的简单值"/>
           <constructr-arg name="属性名" ref="要注入的bean对象"/>
       </bean>
      
    • p名称空间注入:本质是set方法注入
      • bean类里的依赖项,要提供set方法
      • 在配置文件里:
        • 导入p名称空间
        • 在bean标签上使用p:属性名=“简单值” 和 p:属性名-ref="其它bean对象"注入依赖的数据
          <bean id="" class="" p:name="张三" p:userDao-ref="userDao"/>
三.注入集合数据
 要注入集合数据:
     给数组注入:用array标签
     给Set注入:用set标签
     给List注入:用list标签

     给Map注入:用map标签
        给Properties注入:用props标签

        以上标签使用时,所有单列结构的数据,标签可以互换;键值对结构的数据,标签可以互换
 <bean id="collectionService" class="com.user.service.impl.CollectionServiceImpl">
    <property name="array">
        <array>
            <value>a1</value>
            <value>a2</value>
            <value>a3</value>
        </array>
    </property>
    <property name="set">
        <set>
            <value>s1</value>
            <value>s2</value>
            <value>s3</value>
        </set>
    </property>
    <property name="list">
        <list>
            <value>l1</value>
            <value>l2</value>
            <value>l3</value>
        </list>
    </property>
    <property name="map">
        <map>
            <entry key="m1" value="M1"/>
            <entry key="m2" value="M2"/>
            <entry key="m3" value="M3"/>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="p1">P1</prop>
            <prop key="p2">P2</prop>
            <prop key="p3">P3</prop>
        </props>
    </property> 
</bean>
关于注解的IOC
一. 基于注解的IOC(原始注解)
  • 声明bean的注解
    • @Component:可以用在任意类上,把类声明成bean
    • @Controller:用于web层的类上,把类声明成bean
    • @Service:用于service层的类上,把类声明成bean
    • @Repository:用于dao层的类上,把类声明成bean
  • 配置bean的注解
    • 配置作用范围:@Scope
      • @Scope(“singleton”):单例的,默认的
        • 何时创建:Spring容器初始化时
        • 何时销毁:Spring容器关闭时
        • 在整个Spring容器里,只有一个该bean的对象
      • @Scope(“prototype”):多例的
        • 何时创建:获取bean对象时
        • 何时销毁:长时间不用,垃圾回收
        • Spring可以生成多个bean对象,生成之后,交给JVM管理
    • 指定初始化方法:@PostConstruct加在方法上
    • 指定销毁方法:@PreDestroy加在方法上
  • 依赖注入的注解
    • @Autowired:byType注入。会根据依赖项的类型,从Spring容器里查找bean对象注入进来
      • 如果找到一个,直接注入
      • 如果找到多个,会以依赖项的名称为id,查找bean对象注入进来
      • 如果找不到,会报错
    • @Autowired + @Qualifier:byName注入。使用@Qualifier指定要注入的bean对象的id
    • @Resource:byName注入,相当于@Autowired + @Qualifier
    • @Value:用于注入简单值。
      • 如果已经加载了外部的properties文件,可以使用@Value("${properties文件里的key}")
二. 纯注解开发IOC(新注解)
  • 用纯注解方式开发,代替applicationContext.xml文件:
    • 创建一个Java类,类上加注解@Configuration
  • 代替xml的标签context:component-scan/
    • 在配置类上加注解@ComponentScan(basePackages=“com.user”)
  • 代替XML的标签context:property-placeholder/
    • 在配置类上加注解@PropertySource(“classpath:db.properties”)
  • 如果有一些类在jar包,我们不能通过@Component声明bean,怎么办?
    • 在配置类里增加方法,在方法上增加注解@Bean:
      • 把方法的返回值声明成bean对象,
      • 方法的名称是bean对象的id
      • 方法的参数就是bean对象的依赖,Spring默认采用byType注入
      • 在方法参数上使用注解@Qualifier,使用byName注入
三. Spring整合Junit
  • @RunWit
    用在测试类上,用于声明不再使用Junit,而是使用Spring提供的运行环境

  • @ContextConfiguration
    用在测试类上,用于指定Spring配置类、或者Spring的配置文件

  • 要使用以上注解,需要导入jar包依赖:spring-test 和 junit

<dependency>
   <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
   <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

AOP

一. 什么是AOP
  • AOP:Aspect Oriented Programming,面向切面编程。是通过预编译方式(aspectj)或者运行期动态代理(Spring)实现程序功能的统一维护的技术。
  • AOP是OOP(面向对象编程)的技术延续,是软件开发中的一个热点,也是Spring中的一个重要内容。利用AOP可以实现对业务逻辑各个部分之间的隔离,从而使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时提高了开发效率。
二. AOP的作用
  • 作用:不修改源码的情况下,进行功能增强,通过动态代理实现的
  • 优势:减少重复代码,提高开发效率,方便维护
三. AOP的底层实现

常用的动态代理技术有:

  • JDK的动态代理:基于接口实现的
  • cglib的动态代理:基于子类实现的
四. Spring的AOP
相关的概念
  • 目标对象:Target,要增强的对象(待增强的对象)
  • 代理对象:Proxy,增强后得到的代理对象
  • 连接点:JoinPoint,目标对象里可以增强的方法
  • 切入点:PointCut,增强的那个连接点
  • 通知:Advice,功能增强的代码
  • 切面:Aspect,切入点 + 通知
  • 织入:Weaving,把切入点 和 通知 结合,生成代理对象的过程
明确的事情
  • 我们做的:
    • 编写目标对象(目标类,连接点(切入点))
    • 编写通知
    • 配置切面
  • Spring做的
    • 完成织入的过程,根据我们配置的切面(哪个切入点和哪个通知方法结合)生成代理对象
基于XML的AOP
  • 切入点表达式:execution([权限修饰符] 返回值类型 包名.类名.方法名(参数列表))
  • 通知的种类:
    • 前置通知:aop:before,通知方法在切入点方法之前执行
    • 后置通知:aop:after-returning,通知方法在切入点方法之后执行(如果有异常,就不执行了)
    • 异常通知:aop:after-throwing,通知方法在切入点方法异常时执行(如果没有异常,就不执行)
    • 最终通知:aop:after,通知方法在切入点方法之后执行(无论是否有异常,都必定执行)
    • 环绕通知:aop:around,自己定义增加什么样的通知
      • 通知方法,自己写
          public Object aroundMethod(ProceedingJoinPoint pjp){
              Object result = null;
              try{
                  //增加前置通知
                  
                  //自己调用切入点方法
                  result = pjp.proceed(pjp.getArgs());
                  
                  //增加后置通知
              }catch(Throwable t){
                  //增加异常通知
              }finally{
                  //增加最终通知
              }
              
              return result;
          }
          ````
      
    • 配置文件:用aop:around
    • 切入点表达式抽取
      • 用<aop:pointcut id=“唯一标识” expression=“表达式”>定义切入点表达式
      • 在通知标签里使用pointcut-ref属性来引用切入点表达式
基于注解的AOP
  • 通知的种类:注解方式,通知执行的顺序是前置->最终->后置/异常
    • 前置:@Before
    • 后置:@AfterReturning
    • 异常:@AfterThrowing
    • 最终:@After
    • 环绕:@Around
  • 切入点表达式抽取:
    • 定义:在通知类里新增一个方法,在方法上增加注解:@Pointcut(“切入点表达式”)
    • 引用:
      • 引用其它通知类里定义的切入点:@Before(“com.user.adivce.MyAdvice.pc()”)
      • 引用当前通知类时定义的切入点:
        @Before(“pc()”)
五. Spring的事务管理
什么是声明式事务控制
  • 介绍:
    • 声明式事务控制,是采用声明的方式进行事务管理。所谓的声明,指的就是在配置文件中进行配置。
    • 通过声明式(配置)的方式来处理事务,代替编码式事务控制
  • 作用:
    • 事务管理不入侵开发的组件,松耦合
      • 业务逻辑代码中,没有事务的代码,甚至不会意识到正在事务当中。
      • 事实上也应该如此,业务逻辑代码只处理业务功能,事务控制是属于系统层面的服务;如果想要更改事务,只需要在配置文件中重新配置即可
    • 能以模板的方式使用
      • Spring的声明式事务以AOP为基础,但是几乎是固定的配置模板,即使不懂AOP,也可以配置实现事务管理
    • 易维护。
      • 在不需要事务管理的时候,只需要在配置文件中进行修改,即可把事务管理移除掉,而不需要修改源码,方便维护
  • 注意:Spring的声明式事务,底层就是AOP
声明式事务管理
  • 基于XML的声明式事务管理

    步骤:

  1. 创建Maven项目,导入依赖:spring-context, aspectjweaver, spring-jdbc, 驱动,连接池,测试
  2. 编写业务代码:
    • 编写dao层代码:用JdbcTemplate操作数据库
    • 编写service层代码:不需要有任何的事务管理代码
  3. 在applicationContext.xml里配置事务管理
<bean id="txManager" class="DataSourceTransactionMananger的全限定类名">
	<constructor-arg name="dataSource" ref="连接池对象"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
    	<tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <!-- 配置切面 -->
	<aop:advisor advice-ref="txAdvice" pointcut="com.user.service..*.*(..)"/>
</aop:config>
  • 基于注解的声明式事务管理

    步骤

  1. 创建Maven项目,导入依赖spring-context,spring-jdbc,aspectjweaver,驱动,连接池,测试
  2. 编写业务代码
    • 要把编写的类声明成bean对象
    • 在需要事务管理的类/方法上,增加注解:@Transactional
  3. 在配置文件里:
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.user"/>
<!-- 配置事务管理器 -->
<bean id="txManager" class="DataSourceTransactionManager">
	<constructor-arg name="dataSource" ref="连接池对象"/>
</bean>
<!-- 开启事务的注解驱动 -->
<tx:annotation-driven transaction-manager="txManager"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值