Spring之父——Rod Johnson
Spring是一个轻量级Java开发框架,最早由Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(全栈-一站式)轻量级开源框架,为开发Java应用程序提供全面的基础框架支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
2002,首次推出Spring的框架雏形:interface21框架
2004.3.24 Spring以interface21框架为基础重新设计发布Spring1.0版本
Spring是一个免费开源框架
Spring是一个轻量级、非入侵式框架
控制反转(IOC)面向切面编程(AOP)
支持事务处理,对框架整合支持
Spring(是一个IOC和AOP容器):
即容器主动将资源推送给管理的组件,组件只需要选择一种合适的方式来接受资源 ,将bean的创建交给第三方
Spring ioc在创建的时候,通过配置文件去自动创建对象和赋值
// 加载xml后ApplicationContext 去初始化配置的Bean,通过构造方法。反射机制去创建。
ApplicationContext ioc=new ClassPathXmlApplicationContext(“spring-config.xml”);
spring ioc 容器在读读取bean配置之前 容器进行实例化
实例化方式一:BeanFactory(低层)
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader= new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("applicationContext.xml");
Emp emp1 = (Emp) beanFactory.getBean("emp");
实例化方式二:ApplicationContext (几乎所有的场合都是用applictionContext,其为beanFactory的子接口)
applicationContext 其实现类:
- ClassPathXmlApplicationContext 从类路径下加载配置文件
- FileSystemXmlAppplicationContext 从文件系统中加载配置文件
<beans>
<bean id="emp" class="com.Emp">
<property name="empno" value="1"></property>
</bean>
</beans>
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp = (Emp) applicationContext.getBean("emp");
ioc 配置bean:其都单例的
Person person = (Person) applictionContext.getBean("person");
(此类型的bean有多个,报错)
Person person = (Person) applictionContext.getBean(Person.class);
Person person = (Person) applictionContext.getBean("person02",Person.class);
当bean无id时,容器默认以其全类名为id
<beans>
<bean class="com.Emp">
<property name="empno" value="1"></property>
</bean>
</beans>
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp = (Emp) applicationContext.getBean("com.Emp");
当有多个别名时,可用“,”分隔。
index:构造方法参数索引位置
type:数据类型
name:属性名
ref:引用
注:当其构造方法为多个时(方法重载,参数的个数和类型),type=数据类型
1
2
3
需要改变的属性 property (本质为set方法)去处理
则此bean为抽象的不会被实例化。只可被用来继承。此bean就可作为一个模板
设定bean前置依赖的Bean,前置依赖的Bean会在本Bean创建之前实例化,依赖多个Bean 可逗号隔开
bean的创建顺序默认是按配置顺序
改变创建顺序
初始化方法(容器在实例化bean时,bean 执行的方法)
容器在加载时就去实例化bean false:在获取bean时容器才会去实例化-------懒加载
public void setDept(Dept dept) {
this.dept = dept;
}
<bean id="emp" class="com.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.Dept">
<constructor-arg name="deptNo" value="2222"></constructor-arg>
</bean>
工厂不身不需要创建对象,通过静态方法调用
factory-method:创建方法
public static Person createPerson(String name){
Person person=new Person();
person.setName(name);
person.setAge("11");
return person;
}
}
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>形式的标签,称之为自定义标签,自定义标签的解析流程也是 Spring xml扩展点方式之一
除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境
<beans profile="test">
......
</beans>
<beans profile="dev">
......
</beans>
<bean id="empppp" class="com.Emp" autowire="byType" >
<property name="empno" value="111111111"></property>
</bean>
<alias name="empppp" alias="test"></alias>
指定激活环境:
见反射链接
三级缓存机制
Aware接口是一种框架辅助属性注入的一种思想,其框架中也可以看到类似的接口,框架具备高度封装性,我们接触到的一般都是业务代码,一个底层的API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入对象。
BeanPostProcessor
作用范围:所有bean
类似于过滤器,操作空间大
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程
public Object postProcessBeforeInitialization(Object bean, String beanName)--------->init-method方法 之前
public Object postProcessAfterInitialization(Object bean, String beanName) --------->init-method方法 之后
配置实现接口的bean
Spring2.0:
Spring 2.5
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
外部加载配置文件:classpath(固定写法)--加载类路径下的资源
本质:使用注解去代替xml的配置
通过注解完成对象的创建、属性的赋值。需要依赖spring-context,在加入时候会间接的加入依赖spring-aop
注解原理
(通过注解加入到ioc容器中管理)
导入注解包:spring-aop
特定组件:
- @component:基本注解
- @Respository:标识 持久层组件
- @Service :标识 服务层组件
- @Controller: 标识 控制层组件
对于扫描到的组件,Spring 有默认的命名策略。使用非限定类名,第一个字母小写; 也可以在注解中通过Value属性值标识组件的名称 ------如:@component(“asdas”)
@Scope(value = “singleton\prototype”)------不配置默认单例
只有添加扫描包后,其下被配置的扫描组件才会起作用
type:annotation–类型为注解 | assignable–类名
expression:全类名
exclude:排除不要的
include:只扫描要的
若使用include-filter去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖
context:exclude-filter与context:include-filter 相反,用来告知哪些类不需要注册成Spring Bean,同样注意的是:在use-default-filters="false"的情况下,exclude-filter是针对include-filter里的内容进行排除。
关闭默认全部扫描
开启注解:
场景:属性或方法参数
先根据Type查找,当前存在多个类型时,则根据Name去查找
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
@Autowired(required = false)
false时,当找不到则给null ,
true时,找不到则报错
public class Person {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public Person(){}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
}
场景:属性或方法参数,通常与@Autowired 组合使用
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
指定具体id匹配
可组合使用
@Autowired
@Qualifier(“personServiceChilden”)
PersonService personService;
属性或方法参数
先根据Type查找,当前存在多个类型时,则根据Name去查找
场景:属性或方法参数
@Value("xusx")
private String name;
@Value("${name}")
private String name;
public User UserFactory(@Value("${jdbc.url}") String url){。。。}
@Bean
非自定义Bean不能像自定义Bean那样通过@Component进行管理,非自定义Bean要通过工厂的方式实例化。使用@Bena注解。@Bean可指定BeanName ,若不指定就是当前工厂方法名,
当Bean工厂需要注入参数时可使用:
@Component
public class UserFactory {
@Bean
public User UserFactory(@Value("${jdbc.url}") String url,
EmpService empService){
List all = empService.findAll();
System.out.println(all);
System.out.println(url);
User user = new User();
user.setName("test-factory");
user.setAge("1");
return user;
}
}
@Configuration:注解标注为配置类
@Configuration
@ComponentScan("com.yinhai.xusx")
@Import(DataSourceConfig.class)
public class SpringConfiguration {
}
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.yinhai.xusx.mapper")
public class DataSourceConfig {
@Bean("dataSource")
public DataSource dataSource(
@Value("${jdbc.driverClassName}") String driverClassName,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password
) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
注: 配置类下mapper和其mapper.xml必须为相同的路径下,会默认按照mapper路径去查找mapper.xml,否则抛出此异常,若为配置文件则是指定mapper.xml的路径
AnnotationConfigApplicationContext加载配类
ClassPathXmlApplicationContext加载配置文件
ApplicationContext applicationContext= new AnnotationConfigApplicationContext(SpringConfiguration.class);
@Profile:用与类或方法上,标注其属于哪个环境。只有激活当前环境,才会加入spring容器中。不标注环境则任何环境都可以加入spring
@Profile("test)
可以使用以下两种方式指定激活环境:
System.setProperty("spring.profiles.active","test");
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) applicationContext.getBean("person");
@Component("person")
@Profile("test")
public class Person {}
对象:引用类型属性
目的:省去重复的property赋值,其自动匹配。
<bean id="student" class="com.yinhai.Student" autowire="byName">
<property name="name" value="aaa"></property>
<property name="age" value="1"></property>
</bean>
<bean id="school" class="com.yinhai.School">
<property name="schoolName" value="打大学"></property>
</bean>
private String name;
private int age;
private School school;
打印:
shcool 被自动赋值
Student{name='aaa', age=1, school=School{schoolName='打大学'}}
在ByType中,声明的bean类型只能全局唯一,多余会报错。
classpath:只会到你的class路径中查找找文件;
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找.
格式:@Value(“${}”)
@Value("${name}")
private String name;
注:主的配置文件不能包含在通配符的范围内,否则会造成死循环。
@Configuration类似于 beans
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
是指在程序运行期间,将某段代码动态的切入指定方法的指定位置进行的那种编程方式
动态代理技术,在运行期间对目标对象的方法经行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法。
动态代理两种方式:
JDK动态代理:基于接口
public interface UserService {
public void showData();
}
public class UserServiceImpl implements UserService {
@Override
public void showData() {
System.out.println("showData.............");
}
}
public class MyAdvice {
public void add(){
System.out.println("增强···");
}
}
public class MyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
public ApplicationContext applicationContext;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("userService".equals(beanName)){
Object o = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyAdvice myAdvice = (MyAdvice) applicationContext.getBean("myAdvice");
myAdvice.add();
final Object invoke = method.invoke(bean, args);
return invoke;
}
});
return o;
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
效果
Cglib动态代理:基于类
public class PersonAop {
public void read(){
System.out.println("read--------");
}
}
public class PersonAdvice {
public void beforeRead(){
System.out.println("beforeRead");
}
}
// 目标对象
PersonAop personAop = new PersonAop();
// 通知对象
PersonAdvice personAdvice = new PersonAdvice();
// 增强器对象
Enhancer enhancer = new Enhancer();
// 增强器设置父类
enhancer.setSuperclass(personAop.getClass());
// 增强其回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
personAdvice.beforeRead();
Object invoke = method.invoke(personAop, objects);
return invoke;
}
});
// 创建代理对象
PersonAop person1= (PersonAop) enhancer.create();
// 测试
person1.read();
注:静态代理
Spring AOP:对动态代理规范,但其比较笨重
AspectJ:一个专业的AOP的开源框架。Spring框架已经集成了aspect,通过Spring就可以使用。
execution([访问修饰符] 返回值类型 包名.类名.方法名(参数列表))
public class MyAdviceType implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知");
}
}
配置:
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
joinPoint.proceed();
System.out.println("环绕后");
}
public void errorNotice(Throwable e){
System.out.println("异常通知");
}
配置
通知方法执行顺序:
正常:Before After AfterRunning
异常:Before After AfterThrowing
@Aspect
@Component
public class LogUtils {
// 织入配置:切点与通知的结合
@Before("execution(* service.UserServiceImpl.*(..))")
public void beforeMethod(){
System.out.println("前置通知");
}
@Before("execution(public void com.yinhai.service.impl.PersonServiceImpl.test(String))")
public static void test(JoinPoint joinPoint){
// 获取参数
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.asList(args).toString());
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name);
System.out.println("1111111111111111111111111111");
}
Pointcut :重用----抽取了要切入的方法
@After("LogUtils.all()")
public static void test2(JoinPoint joinPoint){
System.out.println("222222222222222");
}
@Pointcut("execution(public void com.yinhai.service.impl.PersonServiceImpl.test(String))")
public static void all(){
// 随便被声明的方法,无实际作用
}
Pointcut+空方法:目的只是为了抽取表达式(空方法为载体),等同于 XML配置的pointcut-ref=“切点表达式id”;为解决复用、维护;使用类名.方法
方法上直接表达式:等同于XML配置的pointcut=“表达式”
AOP使用场景:日志、权限控制、安全控制、是服务控制
/**
* 业务接口
*/
public interface Rent {
void rent();
}
------------------------
/**
* 业务类实现业务接口
*/
public class Host implements Rent{
public void rent() {
System.out.println("出租房租");
}
}
------------------------
/**
* 代理
* 实现其业务接口
* 组合-设置要代理的对象,
* 可自由扩展
*/
public class HostProxy implements Rent {
private Host host;
public void setHost(Host host) {
this.host = host;
}
public void rent() {
printLog();
host.rent();
}
public void printLog(){
System.out.println("扩展日志");
}
}
-----------------------
测试
Host host=new Host();
HostProxy hostProxy=new HostProxy();
hostProxy.setHost(host);
hostProxy.rent();
注:实现接口+组合业务类
场景:尤其是公司的祖传代码,修改功能时不改变原有的基础上进行代理将很完美解决
事务(Transaction)是访问并可能更新数据库中各项数据项的一个程序执行单元(unit)。 事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。事务是逻辑上的一组操作,要么全部成功,要么全部失败。
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
2. 一致性(Consistency
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。
也就是说事务前后数据的完整性必须保持一致。
3. 隔离性(Isolation)
隔离性是指一个事务的执行不能有其他事务的干扰,事务的内部操作和使用数据对其他的并发事务是隔离的,互不干扰。
4. 持久性(Durability)
持久性是指一个事务一旦提交,对数据库中数据的改变就是永久性的。此时即使数据库发生故障,修改的数据也不会丢失。接下来其他的操作不会对已经提交了的事务产生影响。
Spring对事务的管理分为编程式事务和声明式事务管理的两种方式
脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
比如在事务 A 修改数据之后提交数据之前,这时另一个事务 B 来读取数据,如果不加控制,事务 B 读取到 A 修改过数据,之后 A 又对数据做了修改再提交,则 B 读到的数据是脏数据,此过程称为脏读。
不可重复读: 不可重复读是指在数据库访问中,一个事务范围内多次查询却返回了不同的数据值。这是由于在查询间隔中,其他事务修改并提交而引起的
幻读: 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
解决事务并发存在问题:脏读、不可重复读、幻读
isloation属性:指定事务的隔离级别。
解决事务嵌套问题
编程式事务指的是通过编码方式实现事务,编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务基于AOP,将具体业务逻辑与事务处理解耦,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者发生异常时回滚事务。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。
声明式事务有两种方式:
name:切点方法名称
isolation:事务的隔离级别
propogation:事务的传播行为
timeout:超时时间 -1 永久
read-only:是否在只读
----通配符:get开头的 ----设置相关属性
@EnableTransactionManagement 代替 @Bean 才采用非自定义Bean 完成transactionManager的IOC注册 @Transactional(属性配置) 先上代码–起流程都是 dataSouce->sqlSessionFactory->sqlSession(两种) JavaWeb 启动时加载ioc容器—ContextLoaderListener 其本质servletContextLister监听加载 Spring集成Junit测试: 1、导入spring-test坐标 2、@RunWith替换运来的运行期 3、ContextConfiguration 加载配置类或配置文件 4、@AutoWired注入需要测试的对象 5、创建测试方法进行创建
适应在类或者方法上:作用域不同
运行时异常都会回滚,编译时异常不会回滚。
属性:rollbackFor{类.class,类.class}----------让不需要回滚的回滚
noRollbackFor{类.class,类.class}--------让需要回滚的不回滚Spring整合mybatis
新注解: