spring 开源轻量级框架(轻量级->依赖的东西少)
Spring是一站式框架:
spring在javaee三层结构中,每一层都提供了不同的解决技术
-web层:springMVC
-service: spring的ioc
-dao层:spring的jdbcTemplate
spring核心:1、aop:Aspect Oriented Programming 面向切面编程:扩展功能不修改源代码,相当于一个切面插入到源代码前面,或后面,环绕等
AOP采取【横向抽取】机制,取代了传统【纵向继承】体系重复性代码
需求:在添加用户的action中,添加写日志功能
纵向抽取机制:写一个父类,提供写日志功能,子类action调用(缺点:父类变动,子类也要变动)
aop:横向抽取机制:底层使用动态代理方式-
aop底层使用动态代理实现:
①、有接口情况,使用动态代理创建接口实现类代理对象
②、没有接口情况,使用动态代理创建类的子类代理对象(cglib)
使用场景:日志、性能分析
IOC : 控制反转
(个人理解:创建类对象,不需要new,而是通过配置的方法,注入进去)比如一个类,我们需要调用其方法(非静态方法),需要new出类的对象,ioc指不需要new出对象
而是交给spring配置创建类的对象(反射:newInstance)
IOC、DI 区别:
IOC:控制反转,把对象创建交给spring进行配置(反射)
DI : 依赖注入,向类里面的属性中设置值(反射)
关系:依赖注入不能单独存在,需要在IOC的基础上完成操作
IOC 底层原理:
工厂模式解耦合操作:
public class SpringFactory{
public static UserService getUserService(){
return new UserService(); //写死了,任然耦合
}
}
//这样和工厂类耦合
public class UserServlet{
UserService s = SpringFactory.getUserService();
}
IOC 做法:使用xml配置文件:
<bean id="userService" class="net.dhh.service.UserService"/>
//Spring工厂类:
public static Object getUserService(){
//dom4j + 反射
Class clazz = Class.forName(className);
return clazz.newInstance();
}
bean实例化的三种方式 :
1、 使用类的无参构造函数创建(实际项目用的就是这种方式)
<bean id = "User" class="net.dhh.bean.User"/>
如果User类没有提供无参构造方法,将会抛出异常!2、使用静态工厂创建
public class BeanFactory{
//静态方法
public static User getUser(){
return new User();
}
}
<bean id="user" class="net.dhh.bean.BeanFactory" factory-method="getUser"/>
3、通过实例工厂创建:
public class BeanFactory{
//静态方法
public User getUser(){
return new User();
}
}
<bean id="beanFactory" class="net.dhh.bean.BeanFactory"/>
<bean id="user" factory-bean="beanFactory" factory-method="getUser"/>
bean标签的scope属性:
singleton: 默认值,单例(重点)--> context.getBean("user") == context.getBean("user");
prototype: 多例(重点)--> context.getBean("user") != context.getBean("user")
-->struts2的action为多例的,要配置这个属性
-->想起多年前项目的一个bug,两个不同的request请求到struts2的action,由于
-->action没有配置多例,导致了两个request请求的数据混用
request: web项目中,spring创建一个bean对象,将对象存入到request域中
session: web项目中,spring创建一个bean对象,将对象存入到session域中
globalSession : 全局session-->替代品为redis--->单点登录功能
属性注入:
<!--1、使用有参构造注入属性-->
<bean id="demo" class="net.dhh.test.Demo">
<constructor-arg name="username" value="xxx"></constructor>
</bean>
<!--2、使用set方法注入属性-->
<bean id="demo" class="net.dhh.test.Demo">
<!--注入的属性是字符串-->
<property name="username" value="xxx"></property>
</bean>
<!--3、注入对象-->
<bean id="userDao" class="xxx.xxx.xx"></bean>
<bean id="userService class="xx.xx.xx">
<property name="userDao ref="userDao"></property>
</bean>
<!--4、名称空间注入(了解)-->
<!--xmlns:p="http://www.srpingframework.org/schema/p" -->
<bean id="demo" class="net.dhh.test.Demo" p:username="xxx"></bean>
<!--可以注入属性名为username的属性值-->
<!--5、属性注入复杂类型:数组,list, map ,properties -->
<bean id="demo" class="net.dhh.test.Demo">
<!--注入的属性是数组或者list-->
<property name="arrs">
<list>
<value>1<alue>
<value>2<alue>
<value>3<alue>
</list>
</property>
<!--注入的属性是map-->
<property name="mymap">
<map>
<entry key="aa" value="lucy"></entry>
<entry key="bb" value="tom"></entry>
</map>
</property>
<!--注入的属性properties ->
<property name="myprop">
<props>
<prop key="driverclass">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
</props>
</property>
</bean>
Spring初始化:
ApplicationContext context = new ClassPathXmlApplication("bean.xml");
效率很低,struts2 的 action是多例的,如果把创建ApplicationContext对象放到action对象中,每次都要重新加载,效率将会很低
spring初始化就是创建bean.xml中配置的bean对象,一般都是单例的,以后不用创建
解决思想:把加载配置文件和创建对象过程,在服务启动时完成(spring已经做了,开发者只需配置)实现原理:在服务器启动时,会为每一个项目创建ServletContext对象
在ServletContext对象创建时,使用监听器(ServletContextListener)可以具体监听到ServletContext对象在什么时候创建,
在ServletContext对象创建时,加载Spring配置文件,解析配置文件并创建里面的bean对象
把创建的bean对象放到ServletContext中(setAttribute)
在获取对象的时候,从ServletContext中拿(getAttribute)
spring配置文件的加载:
1、服务器启动时,创建对象加载配置文件
2、底层使用监听器,ServletContext对象
spring做了封装,封装了一个监听器,只需要配置监听器就可以实现spring配置文件的加载
<!-- 配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--指定spring配置文件位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:net/dhh/applicationContext.xml</param-value>
</context-param>
spring注解:
<!--开启注解扫描,到包里面扫面类,方法,属性上面是否有注解-->
<context:component-scan base-package="com.clou.douliu.server.mybatis.mapper" />
<!--如果是几个,可以逗号相隔,如果他们在同一个父包里面,也可以只写父包的包名-->
<!--只会扫描属性上面的注解,建议开启上面一个配置就行了,上面那个包含所有-->
<context:annotation-config />
@Component(value="user") //作用在类上,相当于配置:<bean id ="user" class=""></bean>
public class User{
Spring中提供@Component的三个衍生注解(功能到目前是一样的
,写作日期:2017-02-06,spring4)
@Controller : web层
@Service : 业务层
@respository : 持久层
这4个注解的功能都是一样的,这种做法是为了将来考虑,可能今后的版本,spring会对不同的分层做不同的优化
现在建议web层使用@controller,业务层使用@Service,这会使类的分层更加清晰。
使用注解来创建对象:区分单例还是多例
//使用注解来创建对象:区分单例还是多例
@Service(value="user")
@Scope(value="prototype") //如果是Struts2的action,就一定是用多例
public class User{
属性注入的注解:(使用注解,属性就不需要提供set方法了)
@Autowired 和 @Resource
1、都可以完成对象注入
@Autowired private UserDao userDao;
//Autowired 是根据类名来注入,也就是value值可以随便取,使用Autowired都不受影响
@respository(value="UserDao")
public class UserDao{
@Resource(name="userDao")
private UserDao userDao;
//Resource就是通过指定bean id的名字来注入对象,如果
@respository(value="UserDao2")
public class UserDao{
//这样,Resource将注入不进来,会抛出异常
spring切面编程专业术语:
public class UserService{
public void add(){};
public void update(){};
public void delete(){};
public void findAll(){};
}
连接点:类里面哪些方法可以被增强(加功能),这些方法称为
连接点,add,update,delete,findAll都可以增强,都是连接点
切入点:在类里面有很多方法都可以被增强,但实际上我们可能只增强了add,update方法,实际增强的方法称为切入点
通知/增强(Advice): 增强的逻辑,称做Advice,比如要在add中加入写日志的功能,写日志的功能,称为Advice(通知/增强)
前置通知:在方法之前执行(*)
后置通知:在方法之后执行(*)
异常通知:方法出现异常执行
最终通知:在后置之后执行
环绕通知:在方法之前和之后执行(*)
切面:把Advice应用到具体的方法上面,过程称为切面(把增强应用到切入点的过程)
spring的AOP操作:
1、在spring中进行AOP操作,使用aspectj实现①aspectj不是spring的一部分,和spring一起使用进行aop操作。
②spring2.0以后新增了对Aspectj的支持
2、使用Aspectj实现aop有两种方式
①基于aspectj的xml配置(配置很复杂,常使用注解的方式)
②基于aspectj的注解方式
使用表达式配置切入点(要被增强的方法称为切入点)
常用的表达式:
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
访问修饰符--->public private protected ,*代表所有
?--->问号代表的是一个空格
方法名--->指要被增强的方法名,要把包名和类名都带上
参数--->指要被增强的方法的参数,..代表所有的情况
①execution(* net.dhh.service.UserService.add(..))
②execution(* net.dhh.service.UserService.*(..)) //代表UserService下所有的方法
③execution(* *.*(..)) //all of it
④execution(* save*(..)) //匹配所有save开头的方法,没验证过,可能是execution(* *.save*(..))
具体配置:
基于XML配置方式:
在UserService的所有方法中加入写日志的功能:<bean id="userService" class="net.dhh.service.UserService"></bean>
<bean id="logManager" class="net.dhh.util.LogManager"></bean>
<!--配置aop操作-->
<aop:config>
<!--配置切入点(要增强的方法)-->
<aop:pointcut expression="execution(* net.dhh.service.UserService.*(..))" id="pointcut1" />
<!--配置切面(把增强应用到切入点的过程)-->
<aop:aspect ref="logManager">
<!--配置Advice(增强)类型-->
<!--1、增强方法在切入点之前调用-->
<aop:before method="writeLog" pointcut-ref="pointcut1"/> <!--writeLog为LogManager写日志的方法-->
<!--2、增强方法在切入点之后调用-->
<aop:after-returning method="writeLog" pointcut-ref="pointcut1"/>
<!--3、增强方法环绕切入点-->
<aop:around method="writeLog2" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
public class LogManager{
public void writeLog(){
//....
}
//注意:环绕的写法
public void writeLog2(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
//....
//执行要被增强的方法
proceedingJoinPoint.proceed();
//...
}
}
使用注解:
@Aspect
public class LogManager{
//在方法上面使用注解完成增强配置
@Before(value="execution(* net.dhh.service.UserService.*(..))")
public void writeLog(){
//....
}
}
配置打开注解:
<!--开启aop操作-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
个人理解:
aop:把一个方法增强,而不改源代码
增强的方法 -- >增强/Advice -->LogManager.writeLog();
被增强的方法--> 切入点 -- > UserService.*();
把增强加到被增强的方法-->方面
JdbcTemplate:
ORM持久化技术 Spring模板类
JDBC org.springframework.jdbc.core.JdbcTemplate
Hibernate5.0 org.springframework.orm.hibernate5.HibernateTemplate(spring对hibernate框架进行了封装)
MyBatis org.springframework.orm.ibatis.SqlMapClientTemplate
jdbcTemplate对jdbc进行封装,用法和dbUtils相似,对数据进行crud操作。
public void testJdbcTemplate(){
//spring提供的数据源配置
DriverManagerDataResource dataSource = new DriverManagerDataResource();
dataResource.setDriverClassName("com.mysql.jdbc.Driver");
dataResource.setUrl("jdbc:mysql://databaseName");
dataResource.setUsername("root");
dataResource.setPassword("root");
//创建JdbcTemplate对象,设置数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataResource);
String sql = "insert into user value(?,?)";
//增删改都是用update, 后面参数类型Object...
int row = jdbcTemplate.update(sql,"aaa","123");
//得到一个值
//sql = "select * from user";
//int count = jdbcTemplate.queryForObject(sql,Integer.class);
//查询
sql = "select * from user where id =?";
User user = jdbcTemplate.queryForObject(sql,
new RowMapper<User>(){
@Override
public User mapRow(Result rs ,int rowNum) throws SQLExeception{
User u = new User();
u.setId(rs.getInt("id"));
u.setUsername(rs.getString("username"));
return u;
}
},1);
}
JDBC写法:
//JDBC写法:
public void testJDBC(){
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try{
Class.forName("com.mysql.jdbc.Driver");
//创建连接
conn = DriverManager.getConnection("jdbc:mysql:///databaseName","root","root");
//sql
String sql = "select * from user where id = ?";
//预编译sql
psmt = conn.prepareStatement(sql);
//设置参数值
psmt.setInteger(1,1);
//执行sql
rs = psmt.executeQuery();
//遍历
while(rs.next()){
String username = rs.getString("username");
String password = rs.getString("password");
}
}catch(Exception){
}finally{
try{
rs.close();
psmt.close();
conn.close();
}catch(Exception e){
}
}
}
配置C3P0,编码写法:
ComboPooledDataResource dataResource = new ComboPooledDataResource();
dataResource.setDriverClass("com.mysql.jdbc.Driver");
dataResource.setJdbcUrl("jdbc://mysql:///databaseName");
dataResource.setUser("root");
dataResource.setPassword("root");
spring配置c3p0数据源:
<bean id ="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--注入属性值-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc://mysql:///databaseName"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
jdbcTemplate使用:
<bean id="userService" class="net.dhh.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="net.dhh.dao.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--创建jdbcTemplate对象-->
<bean id ="jdbcTemplate" class="org.springframework.jdbc.JdbcTemplate">
<!把数据源注入进来-->
<property name="dataSource" ref="dataSource"></property>
</bean>
Spring事物管理:
spring事物管理两种方式:
第一种:编程事物管理(不同)
第二种:声明式事物管理
①基于xml配置文件实现
<!--第一步,配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--第二步,配置事务增强-->
<tx:advice id="txadvice" transation-manager="transactionManager">
<tx:attributes>
<!--设置进行事务操作的方法匹配规则-->
<!--给insert开头的方法都加上事务管理-->
<!--虽然使用注解的方式配置事务比较简单,但这种方式可以规范开发的方法名-->
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--第三步,配置切面-->
<aop:config>
<!--切入点-->
<!--service所有实现类的所有方法-->
<aop:pointcut expression="execution(* net.dhh.service.impl.*.*(..))" id="pointcut1"/>
<!--切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>
②基于注解实现
<!--第一步,配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--第二步,开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
//第三步,在要使用事务的类上面添加注解
@Transactional
public class OrderService{
}
Spring事务管理器:
Spring为不同的持久化框架提供了不同PlatformTransactionManager(事物管理器)接口实现
1、针对spring jdbc或ibatis进行持久化数据的实现类
org.springframework.jdbc.datasource.DataSourceTransactionManager
2、针对Hibernate5.0版本进行持久化数据的实现类
org.springframework.orm.hibernate5.HibernateTransactionManager
事物的四种隔离级别:
default 使用后端数据库默认的隔离级别
read_uncommited 允许你读取还未提交的改变了的数据,可能导致脏读,幻读,不可重复读
read_commited 允许在并发事务已经提交后读取,可防止脏读,但幻读,不可重复读仍然发生
repeatable_read 对相同字段的多次读取是一致的,除非数据被事务本身改变,可防止脏读,不可重复读,但幻读仍可能发生
serialiazable 完全俯冲acid的隔离级别,确保不发生脏读,幻读,不可重复读。在所有的隔离级别是最慢的.他是典型的通过
锁定在事务中设计的数据表来完成的