Spring

前言

框架

IOC框架(用于解决对象创建,基于控制反转思想):Spring、Android Spring、butterknife
ORM框架(用于处理数据和对象联系,基于对象关系映射):mybatis、Hibernate等

Spring

spring公司开发的一个开源java框架

核心思想

  • 控制反转InversionOfControl
    把对象的控制权(创建、销毁、对象赋值、属性赋值)交给别人(Spring)实施
    依赖注入DependencyInjection(控制反转的实现方式)
    为对象及其内部属性赋值
  • 面向切面编程AspectsOreintedProgramming

作用/优点

  1. 方便解 ,简化开发,提高效率
  1. 支持AOP、IOC
  2. 声明式事物管理
  3. 方便测试(Junit4支持,通过注解测试)
  4. 可以充当容器,支持集成各种开源框架
  5. 封装了常用难用的JAVAEE API,降低其使用难度

核心模块(开发时需要的核心依赖)

spring-Bean 用于Beanfactory装配和对象创建(IOC)
spring-core 核心控件,实现IOC和AOP的基础
spring-context 扩展支持

IOC&DI

XML方式

构造函数创建对象
 @Test
    public void test1(){
        //根据xml文件名加载配置文件(若不指定延迟加载Bean对象,加载xml文件时就创建对象)
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //根据xml文件绝对路径加载配置文件(了解即可)
        ApplicationContext ac2 = new FileSystemXmlApplicationContext("F:\\idea\\D1217\\spring01\\src\\main\\resources\\applicationContext.xml");
        //对象创建原理:XML加载、解析+反射
        //1. xml解析:通过class属性获得全类名
        //2. 反射创建对象: Class.forName("全类名")获取字节码对象---字节码对象.newInstance获得类对象
        //3. 反射注入依赖:字节码对象.getDeclaredFileds获取属性数组,后遍历并setAccessible(true)开启暴力反射,后调用属性对象的set方法赋值
        Person p = ac.getBean("p2",Person.class);
        System.out.println(p.play());
        System.out.println(p);
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    scope 单例模式(对象只创建一次,取多少次Bean得到的对象地址值都一样)-->
<!--                   lazy-init 延迟加载:项目对系统性能要求较高时建议开启,优化内存占用的性能(非延迟加载:确保对象一定存在)-->
<!--          原型模式(每次都重新创建对象,按Bean中配置的属性值为原型,属性一致但地址值不同) -->
<!--    name 名称,不建议重复,创建对象时定位常用-->
<!--    id 唯一标识不可重复-->
<!--    class 创建对象的类-->

    <bean id="p1" name="p1" class="com.zoya.spring.domain.Person" scope="prototype">

    </bean>
    <bean id="p2" name="p2"   class="com.zoya.spring.domain.Person" lazy-init="true">
        <constructor-arg name="age" value="12"></constructor-arg>
        <constructor-arg name="name" value="小辣鸡"></constructor-arg>
    </bean>
</beans>

创建对象时方法的执行顺序

  1. 静态代码块

  2. Bean的生命周期

     2-1. 构造函数(多例模式每次创建对象执行一次,单例模式首次创建执行一次)
     2-2. 初始化方法(每次调用Bean构造对象成功后都会执行1次)
     2-3. 销毁方法 在Bean标签中配置后仍需要使用applicationcontext对象手动调用destroy或close(销毁整个application)
    
工厂方法创建对象
<!--    静态工厂-一步到位地创建工厂对象并调用创建STu对象的【静态】方法,最终返回Stu对象-->
    <bean name="StuStaticFactory" class="com.zoya.factory.StaticFactory" factory-method="createStu" />
<!--    实例工厂:1. 创建工厂对象(为其命名StuInstanceFacBean)-->
    <bean class="com.zoya.factory.InstanceFactory" name="StuInstanceFacBean"></bean>
<!--            2.通过factory-Bean调用前面创建的工厂对象,工厂对象调用创建STU对象的方法,返回STU对象-->
    <bean factory-bean="StuInstanceFacBean" name="StuInstanceFactory" factory-method="createStuInstance"></bean>
public class Demo {
    @Test
    public void test(){
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("appContext.xml");
        //xml文件中直接指定了工厂方法,将在创建完工厂对象后调用工厂方法,返回Student对象
        Student stu = ac.getBean("StuInstanceFactory",Student.class);
        Student stu2 = ac.getBean("StuStaticFactory",Student.class);
        //xml文件中不指定方法等价于以下代码
//        StaticFactory stuFac = ac.getBean("StuStaticFactory",StaticFactory.class);
//        Student stu =  stuFac.createStu();
        System.out.println(stu);
        System.out.println(stu2);
    }
依赖注入DI

不同xml文件中的值互相引用需要在beans内,最前面一行用import的resource属性引入其他xml
同一个bean中可以掺杂不同的注入方式,但不可以重复赋值

  • 配置property,通过set方法注入

      实现原理还是xml解析+反射-调用set方法
    
  • 全参构造函数

      构造函数若存在多个重载(参数类型和数量完全一致,只是顺序不同时),需要通过type指定参数类型或index指定参数位置,以定位唯一的构造方法
    
  • P命名空间
    p(properties) 在文件声明中指定命名空间p
    bean中指定类名
    ----> 在bean标签中 直接通过 p:属性名 直接赋值

在这里插入图片描述

  • SPEL表达式

    “#{a1.name}” xml中name为a1的bean节点的name属性的值
    在这里插入图片描述

  • 复杂类型(集合类型)注入

    <bean name="col1" class="com.zoya.domain.Colles">
<!--     List对应 List(推荐)/array(不推荐)节点-->
        <property name="l" >
<!--            <array>-->
<!--                <value>111</value>-->
<!--                <value>222</value>-->
<!--            </array>-->
            <list>
                <value>111</value>
                <value>222</value>
            </list>
        </property>
<!--    Array数组对应 array节点-->
        <property name="os" >
            <array>
                <value>AAA</value>
                <value>BBB</value>
            </array>
        </property>
<!--    map对应map节点(下设entry节点,用name和value属性存放键值对)-->
        <property name="m" >
            <map>
                <entry key="啦啦" value="~~"/>
                <entry key="哈哈" value="!!"/>
            </map>
        </property>
<!--    set对应set节点(set无序,没有key,直接value赋值即可)-->
        <property name="s" >
            <set>
                <value>(~ ̄▽ ̄)~)</value>
                <value>(#^.^#))</value>
            </set>
        </property>
<!--    properties对应props节点(properties类用于装载properties文件,本质是map)-->
        <property name="p" >
            <props>
                <prop key="url">value1</prop>
                <prop key="driverClassName">value2</prop>
            </props>
        </property>
    </bean>
	结果:
	Colles{s=[(~ ̄▽ ̄)~), (#^.^#))], m={啦啦=~~, 哈哈=!!,}, l=[111, 222], os=[AAA, BBB], p={url=value1, driverClassName=value2}}

注解方式实现

<?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">
<!--以上添加3行关于springframework.org/schema/context的内容-->
<!--    声明需要被扫描注解的包-->
    <context:component-scan base-package="com.zoya">
    </context:component-scan>
</beans>
  • 用于创建对象的注解
  1. @Component(name)声明这是一个组件,将会被创建对象

Component的子类注解:

  1. @Controller(name) 特指控制层组件
  2. @Repository (name)特指Dao层组件(数据访问组件)
  3. @Service (name)特指业务访问组件
  • 用于测试类
  1. @RunWith(SpringJUnit4ClassRunner.class)指定运行测试的类(规定为Runner的子类)

  2. @ContextConfiguration(“classpath:annotation032.xml”)加载要执行的配置文件

     替代原先的手动创建ApplicationContext对象
     可以在一个测试类中形成一个全局的上下文对象,方便调用
    
  • 用于注入值的注解
  1. @Value("") 简单类型赋值(也可以用于set方法)
  2. @AutoWired 自动装配(当变量是一个接口类型时,找其实现类,不配合Qualifier就默认按类型注入)
  3. @Qualifier(“组件名”) 定位自动装配的具体实现类,与AutoWired配合使用就是默认按名称注入
  4. @Resource(name=“组件名”) 等价于 autowired+Qualifier 是java原生注解
  • 用于声明bean的bean属性的注解
  1. @PostConstruct 声明这是个初始化方法
  2. @PreDestroy 声明这是个销毁方法
  3. @Scope(scopeName=“singleton”) 声明作用域
  4. @Lazy(true/false) 延迟加载,默认true

AOP:AspectOrientedProgramming

使代码的颗粒度更细
有利于提高横向层次中代码的复用性
降低耦合度

实现原理:代理模式

要操作的类实现了接口:利用动态代理
要操作的类没有实现接口:利用cglib子代理

  • cglib子代理

      1.导入cglib依赖
      2.创建代理工厂类,实现MethodInterceptor接口
      	2.1 代理工厂类将目标类对象、增强功能类对象、enhancer对象作为私有属性
      		通过有参构造函数传参初始化目标类对象、增强功能类对象;
      		在构造函数中直接调用set初始化enhancer对象
      			setClassLoader--目标类.class.getClassLoader
      			setSuperClass--复制创建的父本,即目标类.class
      			setCallback--MethodInterceptor的实现类对象,即this(设置执行增强的方法)
      	
      	2.2 代理工厂类重写MethodInterceptor接口方法Intercept
      		可以对Method进行筛选过滤,然后调用目标类方法并在前后调用增强类的方法
      	
      	2.3 提供获取代理工厂对象的方法getCgProxyInstance,返回一个enhancer.create()
    
      3.测试使用
      	创建工厂对象,使用getCgProxyInstance获取增强后的代理对象
      	强转成父类-目标类
      	调用方法
    
public class CglibProxy implements MethodInterceptor {
    private UserDaoImpl target;
    private  Transaction adivice;
    private Enhancer enhancer;

    public CglibProxy(UserDaoImpl target, Transaction adivice) {
        this.target = target;
        this.adivice = adivice;

        this.enhancer =new Enhancer();
        Class c = target.getClass();
        enhancer.setSuperclass(c);
        enhancer.setClassLoader(c.getClassLoader());
        //设置增强的回调函数,需要一个MethodInterceptor的实现类(本类就实现了,所以传this即可)
        enhancer.setCallback(this);

    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        Object obj = null;
        System.out.println("执行"+methodName);

        //筛选不需要增强的方法,直接放行
        if("select".equals(methodName)){
            method.invoke(target, objects);
        }else {
            //其他方法前后先执行增强
            adivice.beginT();
           obj = method.invoke(target, objects);
            adivice.commitT();
        }
        return obj;
    }

    public  Object createProxyInstance(){
        //创建一个以target为父类的代理增强对象
        return enhancer.create();
    }
}


public class Demo {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy(new UserDaoImpl(), new Transaction());
        UserDaoImpl ud = (UserDaoImpl)proxy.createProxyInstance();
        ud.select();
    }
}

使用场景

检查网路安全性代码
事务
日志代码
异常处理
权限检查

核心概念

核心概念理解相关概念
切点pointcut选定要切入(增加辅助功能)的位置切点表达式
连接点joinpoint连接增强类和目标类的纽带,作为参数传入通知类的通知方法中,可以调用proceed方法执行目标方法proceedingJoinPoint等子类
目标对象Target核心代码所在的类,要被增强的类
增强/通知方法Advice非核心的,辅助增强的代码引介;切面类
织入Weaving是一个过程,实现“把增强添加到具体连接点”的过程
  • 切点表达式
关键字(权限修饰符返回值全类名.方法名(参数类型))
可省略返回值可以用通配符确定范围大小参数类型可以用..表示通配
execution(**com.zoya.service.UserSe*.insert(..))

使用

XML方式
//接口
public interface UserService {
    int insert();
}
//实现类
public class UserServiceImpl implements UserService {
    @Override
    public int insert() {
        System.out.println("添加用户");
        return 6;
    }
}
//增强类
public class Advice {
    public void before(){
        System.out.println("----前置通知----");
    }
    public void after(){
        System.out.println("----最终通知----");
    }
    //环绕通知一旦显示写出,必须在其中调用proceed才能执行目标方法,并且必须返回目标方法的返回值
    public Object around(ProceedingJoinPoint pj){
        Object obj = null;
        try {
            obj = pj.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("----环绕通知----"+obj);
        return obj;
    }
    public void afterThrowing(){
        System.out.println("----异常通知----");
    }
    public void afterReturning(){
        System.out.println("----后置通知----");
    }
}
//测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:aop2.xml")
public class demo {
    @Autowired
    private UserService us;

    @Test
    public void test(){
       int i = us.insert();
        System.out.println(i);
    }
}
/*
执行结果:
----前置通知----
添加用户
----环绕通知----6
----后置通知----
----最终通知----
6
*/
	1.前置通知
	一般用于检查网络环境
	
	2.1环绕通知前半部分
	一般用于身份校验、权限检查等
		if(符合条件){
			3.执行目标方法(proceed)
		}else{
			3.其他操作
		}
		
	2.2.环绕通知后半部分
	
	4.后置通知(若目标方法无异常/异常已被捕获处理)
	4.异常通知(若目标方法有异常且未被捕获而是被抛出)
	一般用于信息采集、写入日志文件

	5.最终通知
	一般用于资源关闭等


使用案例:
	权限判断——使用环绕通知
	用户访问A页面时,以访问方法为切点,织入环绕通知(环绕通知中先判断用户权限,有权限再执行proceed,没有权限执行其他内容)
注解方式

执行顺序

多个通知方法的执行顺序

  1. 前:环绕前/前置

  2. 中:目标方法(若调用了proceed)

  3. 后:环绕后/后置/异常/最终

    xml中同级通知之间执行顺序与注册顺序(配置顺序)有关

    注解方式中默认 环绕前–前置–目标–环绕后–最终–后置

多个切面类的执行顺序

  1. 优先级高的在外层(最前最后)
  2. 默认按切面类类名的字母顺序,a-z优先级递减
  3. 手动配置xml中 aop:aspect 标签的order属性
    在切面类上使用@Order注解;
    数值越小优先级越高(支持负数)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值