-
spring学习
spring相关知识#
spring相关概述#
- 轻量级的Javaee开源框架,解决企业开发的复杂性
- 核心特性ioc和aop
ioc 控制翻转,把创建对象交给spring管理
aop 不修改源代码就行功能增强
-
特点
方便接偶,简化开发
aop编程支持
方便与其他框架整合
方便进行事务操作
入门案例#
- 创建maven项目
- 导入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
- 进行测试
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 获取创建的对象
User user = context.getBean("user", User.class);
概念和原理#
- ioc
控制翻转,把对象创建和对象之间的调用过程交给spring管理,降低耦合度,入门案例就是ioc的说过话i先
- 底层原理
xml解析,工厂模式,反射
原始通过new的方式来进行创建对象,当serveic中创建dao的时候,dao的路径发生了变化,service中也要进行路径的更改,可以通过工厂模式进行降低相互之间的依赖
此时工厂和dao之间产生了耦合,可以使用xml或者properties进行进一步解耦,spring中使用了xml的方式进行。
ioc的容器本质上就是对象工厂,spring在启动的时候解析xml配置文件,根据配置的规则创建出对象就行存放到工厂中,然后进行相应读取,默认是立即加载,也可以配置懒加载。
对象的两个接口beanFactory
和applicationContext
,前者是spring内部使用的接口,后者是扩展类,提供了更强大的功能。前者是延迟加载,后者是立即加载。
常见的实现类为ClassPathXmlApplicationContext
,FileSystemXmlApplicationContext
,前者是类路径,后者是绝对路径。
3. bean管理的主要内容:
创建对象(xml配置方式/注解方式),注入属性。
基于xml创建对象
在spring配置文件中,使用bean标签,标签里面添加对应的属性,可以实现。
id属性表示唯一标识,classs属性为类全路径,后续spring根据改class的值通过反射默认午餐方式进行床架接你对象
基于xml方式注入属性
-
通过set方式进行注入(常用)
<property name="name" value="demo"/>
-
通过有参构造
<constructor-arg name="name" value="demo"></constructor-arg>
-
通过p标签进行注入本质通过set方式进行注入,语法糖的效果
需要导入命名空间
<bean id="user" class="com.haha.spring.User" p:name="demo"/>
注入null值
<property name="name"><null/></property>
注入特殊的符号,使用<![CDATA[]]>
包含
<bean id="user" class="com.haha.spring.User" >
<property name="name">
<value><![CDATA[<你好>]]></value>
</property>
</bean>
- 属性的赋值
<bean id="emp" class="com.haha.spring.bean.Emp">
<property name="ename" value="demo"/>
<property name="gender" value="mail"/>
<!--通过set的方式-->
<property name="dept" ref="dept"></property>
<!--本质是先get出来dept的属性,不常用,emp中要有getDept的方法,然后再调用dept的setDname进行复制-->
<property name="dept.dname" value="技术"/>
</bean>
- 注入数组集合map相关的属性
<bean id="course01" class="com.haha.spring.bean.Course"/>
<bean id="stu" class="com.haha.spring.bean.Stu">
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<property name="maps">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<property name="sets">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="courseList">
<list>
<!--内部复制-->
<bean id="course" class="com.haha.spring.bean.Course">
<property name="cname" value="demo"/>
</bean>
<!--引用外面的对象-->
<ref bean="course01"/>
</list>
</property>
</bean>
- 对象的创建(可以通过depondon来改变创建的顺序)
- 普通的bean对象
-
bean工厂的对象(此时分为静态工厂和示例的工厂)
普通的bean直接通过bean
标签获取的类型和class中定义的类型相同。还有一种方式是bean需要实现beanFactory
的接口,然后实现里面的方法,通过xml的方式进行注册。
-
bean的作用域
默认情况下是单例的模式,可以通过scope
的标签进行设置。可选的值为singleton
,prototype
。后者是懒加载的方式,在获取的时候才会进行示例的创建。 - bean的生命中周期
- 默认调用无参构造就行创建对象
- 调用set方法进行属性的注入
- 把bean的实例传递给后置处理器前置方法
- 执行初始化方法
- 把bean的周期传递给后置处理器的后置方法
- 获取创建bean的示例对象
- 在销毁的时候执行销毁的方法
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建bean实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用set方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
}
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
// 加载spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo2.xml");
// 获取创建的对象
Orders orders = context.getBean( Orders.class);
context.close();
<bean id="order" class="com.haha.spring.demo2.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="demo"></property>
</bean>
<!--配置后置处理器,会对所有创建的对象生效-->
<bean class="com.haha.spring.demo2.bean.MyBeanPost"/>
-
bean的自动装配
可以根据名称或者属性的类型就行相应的自动的装配,为后续注解式的注入打下基础
<bean id="dept" class="com.haha.spring.bean.Dept"/>
<bean class="com.haha.spring.bean.Emp" id="emp" autowire="byType"/>
- 操作外部属性的文件
<!--引入命名空间-->
<context:property-placeholder location="jdbc.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="name" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="url" value="${prop.url}"/>
<property name="driverClassName" value="${prop.driverClass}"/>
</bean>
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
基于注解的方式创建对象
基于注解的方式可以简化xml的配置,需要有spring-aop
包的支持
-
导入
spring-aop
包的支持 - 开启注解的扫描
-
在需要配置的累上添加相应的注解
默认的filter是扫描配置包下的所有类,里面有各种的扫描类型,可以进行配置,如果需要详细的信息,点击此处
-
基于注解的方式实现属性的注入
autowire
按照类型进行匹配,如果改类型有多个示例,按照属性的名称进行相应的查找,如果查找不到跑出异常,后面可以在添加上qualifiter
进行按照名称进行查询,是spring
提供的注解,也可以使用resource
进行相应的注入,这是Java
规范提供的注解。默认根据名称进行注入,也可以指定bytype属性根据类型进行相应的注入。vaule
注解可以注入普通类型的属性。 - 完全注解的开发
@Configuration
@ComponentScan(basePackages = "com.haha.spring")
public class SpringConfig {
// 配置相应的属性对象,类似于xml中的bean标签
}
// 加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
context.getBean("xxx");
带泛型的依赖注入
在对象注入的时候可以根据不同泛型进行注入
aop的相关知识#
利用aop可以对业务逻辑的各个部分进行相应的隔离,从而达到各部分之间的耦合度降低,提高程序的可用性,提高开发的效率。不通过修改源码的方式,在主干的功能里面添加新的功能。
aop底层使用动态代理的方式进行实现。
- 有接口的情况,创建接口的实现类对象,增强累的方法。
-
没有接口的情况,使用CGLIB进行相应的动态代理,通过继承该类的方式。
jdk的动态代理
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
Object proxyInstance = Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "");
Object o = method.invoke(new UserDaoImpl(), args);
return o;
}
});
UserDao dao = (UserDao) proxyInstance;
dao.add(1,2);
}
}
- aop相关术语
- 连接点 类中可以被增强的方法
- 切入点 类中真正增强的方法
-
通知 实际增强的逻辑部分称为通知
通知类型
前置,后置,环绕,异常,最终通知
4. 切面:把通知应用到切入点的过程
切入点表达式
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
访问修饰符可以省略
-
com.haha.demo....*(..)表示demo包以及子包下的所有类的所有方法。
*..表示当前的包以及子包
可以通过order
注解进行改变切面的顺序,值越小优先级越高
aop的注解配置
<!--一般都是通过aspectj来实现aop的功能-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
<scope>test</scope>
</dependency>
<context:component-scan base-package="com.haha.spring"/>
<!--开启基于注解的AOP功能-->
<aop:aspectj-autoproxy/>
/**
* @Before: 前置通知, 在方法执行之前执行
* @After: 后置通知, 在方法执行之后执行
* @AfterRunning:返回通知, 在方法成功执行返回结果之后执行
* @AfterThrowing: 异常通知, 在方法抛出异常之后
* @Around: 环绕通知, 围绕着方法执行
*/
@Aspect
@Component
public class LogUtil {
@Pointcut("execution(* com.haha.spring.*.*(..))")
public void myPoint(){};
//定义通知方法
@Before(value = "myPoint()")
public void LogBefore(JoinPoint joinPoint) {
System.out.println("LogBefore方法被执行了...,方法名称"+joinPoint.getSignature().getName());
}
@After(value = "myPoint()")
public void LogAfter() {
System.out.println("LogAfter方法被执行了...");
}
@AfterReturning(value = "myPoint()",returning = "end")
public void LogAfterReturning(Object end) {
System.out.println("LogAfterReturning方法被执行了..."+end);
}
@AfterThrowing(value = "myPoint()",throwing = "throwing")
public void logThrowing(Exception throwing){
throwing.printStackTrace();
}
}
@SpringJUnitConfig(locations = "classpath:demo.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
@Autowired
User user;
@Test
public void test01(){
System.out.println(user.add(1, 2));
}
}
环绕通知
本身就是一个动态代理。可以改变返回值
@Around("myPoint()")
public Object around(ProceedingJoinPoint joinPoint){
try {
System.out.println("环绕方法的前置");
Object o = joinPoint.proceed();
System.out.println("环绕方法的后置");
return o;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕方法的异常");
return null;
}finally {
System.out.println("环绕方法的最终通知");
}
}
aop的xml配置
<!--一般都是通过aspectj来实现aop的功能-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--1.将目标类与切面类都加入到ioc容器中-->
<bean id="userServlet" class="pers.lele.servlet.UserServlet"></bean>
<bean id="logUtils" class="pers.lele.utils.LogUtils"></bean>
<bean id="timeUtils" class="pers.lele.utils.TimeUtils"></bean>
<!--2.告诉Spring哪个是切面类-->
<aop:config>
<!--切入点-->
<aop:pointcut id="deleteUserById"
expression="execution(public * pers.lele.servlet.UserServlet.deleteUserById(Integer))"/>
<!--2.告诉Spring哪些是切面类-->
<aop:aspect ref="logUtils">
<!--3.告诉Spring通知方法何地运行-->
<aop:before method="LogBefore" pointcut-ref="deleteUserById"/>
<aop:after-returning method="LogAfterReturning" pointcut-ref="deleteUserById" returning="result"/>
<aop:after-throwing method="LogAfterThrowing" pointcut-ref="deleteUserById" throwing="e"/>
<aop:after method="LogAfter" pointcut-ref="deleteUserById"/>
<aop:around method="LogAround" pointcut-ref="deleteUserById"/>
</aop:aspect>
<aop:aspect ref="timeUtils">
<aop:before method="LogBefore" pointcut-ref="deleteUserById"/>
<aop:after-returning method="LogAfterReturning" pointcut-ref="deleteUserById" returning="result"/>
<aop:after-throwing method="LogAfterThrowing" pointcut-ref="deleteUserById" throwing="e"/>
<aop:after method="LogAfter" pointcut-ref="deleteUserById"/>
</aop:aspect>
</aop:config>
事务的相关知识#
<!--1.配置事务管理器,让其进行事务控制-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.开启基于注解的事务控制模式,依赖tx名称空间 需要aop包-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
transaction注解的详细配置
xml的声明式事务
- 配置事务管理起
- 配置通知
-
配置切入点和切面
事务管理器本质是一个切面类。