1.spring的jar包下载
2.在src下新建一个文件夹resources,放个配置文件applicationContext.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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描注解类 -->
<context:component-scan base-package="tcc"></context:component-scan>
<!-- <context:component-scan base-package="tcc.test.springioc"></context:component-scan> -->
<!-- 确定 AOP注解生效 -->
<!-- <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,
当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。
不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
3.写个ioc测试类
package tcc.test.springioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@SuppressWarnings("resource") //抑制警告,打断点不影响
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
Student student = ac.getBean("student", Student.class);
student.eat();
}
}
测试bean
package tcc.test.springioc;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Scope("prototype")
//singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例
//prototype表示每次获得bean都会生成一个新的对象
//request表示在一次http请求内有效(只适用于web应用)
//session表示在一个用户会话内有效(只适用于web应用)
//globalSession表示在全局会话内有效(只适用于web应用)
//在多数情况,我们只会使用singleton和prototype两种scope,如果在spring配置文件内未指定scope属性,默认为singleton。
@Component("student")//类注解
public class Student {
public void eat(){
System.out.println("Student类新增了");
}
}
Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
BeanFactory和ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
(1)BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
(2)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误
BeanFactory需要手动注册,而ApplicationContext则是自动注册。
Spring框架中的单例Beans是线程安全的么?
Spring框架并没有对单例bean进行任何多线程的封装处理。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
9、Spring如何处理线程并发问题?
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
创建对象的时机
1、在默认的情况下,通过xml配置的在spring容器启动的时候创建对象
在spring配置文件中,只要根据以上的三种方式的其中一种配置了,spring容器就会创建对象
好处:spring容器和web容器整合的时候,当web容器启动的时候就可以初始化spring容器了,如果这个时候
spring容器内部有错误,则直接会报错
如果该bean中存放着大量的数据,而且数据的初始化发生在创建对象的时候,这个时候,数据会过早的驻留在内存中
2、如果在spring的配置文件中一个bean的配置中有lazy-init="true",那么该bean在调用getBean方法时创建对象
不好处:不能过早的发现错误
好处:按照需求加载数据(什么时候要什么时候加载)
<bean id="ms1" class="basic.MassageService" init-method="in" destroy-method="des"/>
各种对象,都是在tomcat启动时创建,Tomcat怎么知道的?
tomcat启动时 会把每一个项目的所有代码扫描一遍,需要它启动时就创建的,就创建对象。
Tomcat在启动时都创建了哪些对象?慢慢总结如下:
servletContext(一个项目的全局变量)、Servlet(load-on-startup为非负数时)、Filter(整个项目的过滤器)
写个aop测试类
1.
package tcc.test.springaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
Person person = ac.getBean("person", Person.class);
person.add();//调用一次MyAspectJ类
person.update();//调用一次MyAspectJ类
person.delete();//调用一次MyAspectJ类
}
}
2.
package tcc.test.springaop;
public interface Person {
public void add();
public void update();
public void delete();
}
3.
package tcc.test.springaop;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tcc.test.springioc.Student;
@Component("person")//类注解
public class PersonImple implements Person {
/**
* @desc 实现接口方法
*/
@Autowired //默认按类型装bai配(这个注解是属业spring的)
Student student;
@Resource //这个注解属于J2EE的),默认按照名称进行装配
Student student2;
public void add() {
student2.eat();
student.eat();
System.out.println("add()方法执行了.....");//3
int i = 1/0;
}
@Override
public void update() {
System.out.println("update()方法执行了.....");
// int i = 1/0;
}
@Override
public void delete() {
System.out.println("delete()方法执行了.....");
}
}
4.
package tcc.test.springaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component//类注解
@Aspect//AspectJ注解
public class MyAspectJ {
//声明公共切入点
@Pointcut("execution(* tcc.test.springaop.PersonImple.*(..))")
public void myPointcut() {
}
//前置通知注解,只有一个参数时,value可以省略不写
@Before("execution(* tcc.test.springaop.PersonImple.*(..))")
public void myBefort(JoinPoint joinPoint) {
System.out.println("前置通知>>>>>>>>>joinPoint: " + joinPoint.getSignature().getName());//2:会去调用具体的方法
}
//后置通知注解,当参数大于1时,value必须写
@AfterReturning(value="myPointcut()", returning="ret")
public void myAfterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("后置通知>>>>>>>>>joinPoint: " + joinPoint.getSignature().getName()//4
+ ", ret: " + ret);
}
//环绕通知注解
@Around("myPointcut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知====前>>>>>>>>>>>");
Object obj = joinPoint.proceed();//1
System.out.println("环绕通知====后<<<<<<<<<<<");
return obj;
}
//异常通知注解:出现异常后调用
@AfterThrowing(value="myPointcut()", throwing="e")
public void myThrowint(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知>>>>>>>>>joinPoint: " + joinPoint.getSignature().getName() //4
+ ", e: " + e.getMessage());
System.exit(0);
}
//最终通知注解
@After("myPointcut()")
public void myAfter(JoinPoint joinPoint) {
System.out.println("最终通知>>>>>>>>>joinPoint: " + joinPoint.getSignature().getName());//3
}
}
执行顺序:环绕通知注解@Around("myPointcut()")——》前置通知注解@Before("execution(* tcc.test.springaop.PersonImple.*(..))")——》最终通知注解@After("myPointcut()")——》异常通知注解:出现异常后调用@AfterThrowing(value="myPointcut()", throwing="e")或者后置通知注解@AfterReturning(value="myPointcut()", returning="ret")
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。实现的方式做的动态代理
②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。