Spring 框架基础
一、Spring框架
1、框架简介
Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。简单来说,Spring是一个分层的轻量级开源框架。
2、优点分析
1)、分层架构
一站式,每一个层都提供的解决方案
web层:struts,spring-MVC
service层:spring
dao层:hibernate,mybatis,jdbcTemplate,JPA
2)、轻量级
依赖资源少,销毁的资源少。
3)、高内聚低耦合
Spring就是一个大容器,可以将所有对象创建和依赖关系统一维护,交给Spring管理。
4)、AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
5)、事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
6)、集成测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序。
7)、降低API的使用难度
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
8)、集成各种框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的集成,如:Struts、Hibernate、MyBatis等。
二、核心组件分析
1、核心容器
容器是Spring框架的核心模式,该模块包含Bean的创建、配置、管理等功能。
2、AOP编程
AOP 编程可以帮助应用程序解耦,使用AOP编程模式,可以把系统中的核心点从对象方法中解耦,统一管理。
3、数据访问
该模块集成了JDBC,解决JDBC开发模式导致的大量代码冗余,集成常用的Dao层框架,hibernate,mybatis等,使开发环境的搭建更加便捷。
4、Web编程
Spring不仅集成各种流程的MVC框架,还自带springmvc强大的框架,有助实现界面逻辑和应用程序分离,在Web层面实现应用的解耦。
三、环境搭建
项目结构图:
1、Spring环境配置
- spring-context
Spring框架上下文环境容器配置。
<!--读取外部配置文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 允许JVM参数覆盖 -->
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<!--忽略没有找到的配置参数-->
<property name="ignoreResourceNotFound" value="true"/>
<!--资源文件位置-->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- 启动组件扫描,排除@Controller组件,该组件由SpringMVC配置文件扫描 -->
<context:component-scan base-package="com.spring.mvc">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- 配置DRUID的连接池 -->
<bean id="druidDataSource" abstract="true">
<!-- 配置初始化,最小,最大 -->
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}" />
<property name="maxActive" value="${jdbc.maxActive}" />
<!-- 配置连接等待超时时间 -->
<property name="maxWait" value="${jdbc.maxWait}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中的最小生存时间,单位毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 配置监控统计拦截的filters,去掉后监控界面SQL无法统计 -->
<property name="filters" value="stat" />
</bean>
<!-- 设置数据源信息 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" parent="druidDataSource">
<!-- 配置连接的基本信息 -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!--Spring整合mybatis-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--读取mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<!-- 自动扫描mapper.xml映射文件 -->
<property name="mapperLocations" value="classpath:mybatis/mapper/**.xml"/>
<!--分页助手插件-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<value>
dialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!-- Mapper接口文件扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.spring.mvc.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--设置JDBC操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" lazy-init="false">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--设置mybatis操作数据库-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--方式一:spring事物管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!--配置手动事物管理-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="dataSourceTransactionManager"/>
</bean>
- spring-mvc
Mvc开发环境容器配置。
<!--扫描文件-->
<context:component-scan base-package="com.spring.mvc.controller" />
<!-- MVC默认的注解映射的方式 -->
<mvc:annotation-driven />
<mvc:default-servlet-handler/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/" />
<property name="suffix" value=".jsp" />
</bean>
2、jdbc参数配置
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring-mvc
jdbc.username=root
jdbc.password=123
jdbc.initialSize=10
jdbc.minIdle=10
jdbc.maxActive=120
jdbc.maxWait=60000
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.minEvictableIdleTimeMillis=300000
3、mybatis配置
mybatis-config.xml文件
<configuration>
<settings>
<!--打印sql语句-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
4、web.xml配置
<display-name>frame_spring</display-name>
<!--Spring相关配置-->
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 编码过滤器,以UTF8编码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5、pom.xml配置
<build>
<finalName>${pom.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
=====================================================================
Bean的生命周期,作用域,装配总结
一、装配方式
Bean的概念:Spring框架管理的应用程序中,由Spring容器负责创建,装配,设置属性,进而管理整个生命周期的对象,称为Bean对象。
1、XML格式装配
Spring最传统的Bean的管理方式。
- 配置方式
<bean id="userInfo" class="com.spring.mvc.entity.UserInfo">
<property name="name" value="cicada" />
</bean>
- 测试代码
ApplicationContext context01 = new ClassPathXmlApplicationContext("/bean-scan-02.xml");
UserInfo userInfo = (UserInfo)context01.getBean("userInfo") ;
System.out.println(userInfo.getName());
2、注解扫描
在实际开发中:通常使用注解 取代 xml配置文件。
- 常见注解
@Component <==> <bean class="Class">
@Component("id") <==> <bean id="id" class="Class">
@Repository :Mvc架构中Dao层Bean的注解
@Service:Mvc架构中Service层Bean的注解
@Controller:Mvc架构中Controller层Bean的注解
- 使用案例
// 1、注解代码块
@Component("infoService")
public class InfoServiceImpl implements InfoService {
@Override
public void printName(String name) {
System.out.println("Name:"+name);
}
}
// 2、配置代码块
@ComponentScan // 组件扫描注解
public class BeanConfig {
}
- 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanConfig.class)
public class Test01 {
@Autowired
private InfoService infoService ;
@Test
public void test1 (){
infoService.printName("cicada");
System.out.println(infoService==infoService);
}
}
3、XML配置扫描
上面使用 ComponentScan 注解,也可在配置文件进行统一的配置,效果相同,还简化代码。
<context:component-scan base-package="com.spring.mvc" />
4、Java代码装配
这种基于Configuration注解,管理Bean的创建,在SpringBoot和SpringCloud的框架中,十分常见。
- 配置类代码
@Configuration // 配置类注解
public class UserConfig {
@Bean
public UserInfo userInfo (){
System.out.println("userInfo...");
return new UserInfo() ;
}
}
- 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = UserConfig.class)
public class Test03 {
@Autowired
private UserInfo userInfo ;
@Autowired
private UserInfo userInfo1 ;
@Test
public void test1 (){
/*
* userInfo...
* true
*/
System.out.println(userInfo==userInfo1);
}
}
二、属性值设置
上面是Bean的装配几种常见方式,下面来看看Bean属性值设置,这里就基于Xml配置的方式。
1、基础类型和集合
- 配置代码
<!-- 配置Employee公共属性 -->
<bean id="emp1" class="com.spring.mvc.entity.Employee">
<property name="name" value="cicada" />
<property name="id" value="1" />
</bean>
<bean id="emp2" class="com.spring.mvc.entity.Employee">
<property name="name" value="smile" />
<property name="id" value="2" />
</bean>
<!-- 配置Department属性 -->
<bean id="department" class="com.spring.mvc.entity.Department">
<!-- 普通属性值的注入 -->
<property name="name" value="IT部门" />
<!-- 给数组注入值 -->
<property name="empName">
<list>
<value>empName1</value>
<value>empName2</value>
<value>empName3</value>
</list>
</property>
<!-- 给List注入值:可以存放相同的值 -->
<property name="empList">
<list>
<ref bean="emp1"/>
<ref bean="emp2"/>
<ref bean="emp1"/>
</list>
</property>
<!-- 配置Set属性,相同的对象会被覆盖 -->
<property name="empSet">
<set>
<ref bean="emp1"/>
<ref bean="emp2"/>
<ref bean="emp1"/>
</set>
</property>
<!-- 配置Map属性,key相同的话,后面的值会覆盖前面的 -->
<property name="empMap">
<map>
<entry key="1" value-ref="emp1" />
<entry key="2" value-ref="emp2" />
<entry key="2" value-ref="emp1" />
</map>
</property>
<!-- 配置属性集合 -->
<property name="pp">
<props>
<prop key="pp1">Hello</prop>
<prop key="pp2">World</prop>
</props>
</property>
</bean>
- 测试代码
public class Test05 {
@Test
public void test01 (){
ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-03.xml");
Department department = (Department) context.getBean("department");
System.out.println(department.getName());
System.out.println("--------------------->String数组");
for (String str : department.getEmpName()){
System.out.println(str);
}
System.out.println("--------------------->List集合");
for (Employee smp : department.getEmpList()){
System.out.println(smp.getId()+":"+smp.getName());
}
System.out.println("--------------------->Set集合");
for (Employee emp : department.getEmpSet()){
System.out.println(emp.getId()+":"+emp.getName());
}
System.out.println("--------------------->Map集合");
for (Map.Entry<String, Employee> entry : department.getEmpMap().entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue().getName());
}
System.out.println("--------------------->Properties");
Properties pp = department.getPp();
System.out.println(pp.get("pp1"));
System.out.println(pp.get("pp2"));
}
}
2、配置构造函数
根据配置的参数个数和类型,去映射并加载Bean的构造方法。
- 配置代码
<!-- 这里配置2个参数,所有调用2个参数的构造函数 -->
<bean id="employee" class="com.spring.mvc.entity.Employee">
<constructor-arg index="0" type="java.lang.String" value="cicada"/>
<constructor-arg index="1" type="int" value="1"/>
</bean>
- 测试代码
public class Test06 {
@Test
public void test01 (){
ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-04.xml");
Employee employee = (Employee) context.getBean("employee");
System.out.println(employee.getId()+":"+employee.getName());
}
}
3、配置继承关系
- 配置代码
<!-- 配置父类信息 -->
<bean id="student" class="com.spring.mvc.entity.Student">
<property name="name" value="Spring" />
<property name="age" value="22" />
</bean>
<!-- 配置子类信息 -->
<bean id="grade" class="com.spring.mvc.entity.Grade">
<!-- 覆盖 -->
<property name="name" value="Summer" />
<property name="degree" value="大学" />
</bean>
- 测试代码
public class Test07 {
@Test
public void test01 (){
ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-05.xml");
Grade grade = (Grade) context.getBean("grade");
/* Summer;0;大学 */
System.out.println(grade.getName()+";"+grade.getAge()+";"+grade.getDegree());
}
}
三、作用域
作用域:用于确定spring创建bean实例个数,比如单例Bean,原型Bean,等等。
类型 | 说明 |
---|---|
singleton | IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个单例Bean实例,默认配置。 |
prototype | IOC容器可以创建多个Bean实例,每次返回的Bean都是新的实例。 |
request | 每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。 |
session | 同一个HTTP Session共享一个Bean实例。不同HTTP Session使用不同的实例。 |
global-session | 同session作用域不同的是,所有的Session共享一个Bean实例。 |
四、生命周期
在Spring框架中Bean的生命周期非常复杂,过程大致如下:实例化,属性加载,初始化前后管理,销毁等。下面基于一个案例配置,会更加的清楚。
1、编写BeanLife类
public class BeanLife implements BeanNameAware {
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("设置名称:"+name);
this.name = name;
}
@Override
public void setBeanName(String value) {
System.out.println("BeanNameAware..SetName:"+value);
}
public void initBean() {
System.out.println("初始化Bean..");
}
public void destroyBean() {
System.out.println("销毁Bean..");
}
public void useBean() {
System.out.println("使用Bean..");
}
@Override
public String toString() {
return "BeanLife [name = " + name + "]";
}
}
2、定制加载过程
实现BeanPostProcessor接口。
public class BeanLifePostProcessor implements BeanPostProcessor {
// 初始化之前对bean进行增强处理
@Override
public Object postProcessBeforeInitialization(Object obj, String beanName) throws BeansException {
System.out.println("初始化之前..."+beanName);
return obj ;
}
// 初始化之后对bean进行增强处理
@Override
public Object postProcessAfterInitialization(Object obj, String beanName) throws BeansException {
System.out.println("初始化之后..."+beanName);
// 改写Bean的名称
if (obj instanceof BeanLife){
BeanLife beanLife = (BeanLife)obj ;
beanLife.setBeanName("myBeanLifeTwo");
return beanLife ;
}
return obj ;
}
}
3、配置文件
<!-- 加载Bean的处理器 -->
<bean class="com.spring.mvc.BeanLifePostProcessor" />
<!-- 指定初始化和销毁方法 -->
<bean id="beanLife" class="com.spring.mvc.entity.BeanLife"
init-method="initBean" destroy-method="destroyBean">
<property name="name" value="myBeanLifeOne" />
</bean>
4、测试过程
- 测试代码
public class Test08 {
@Test
public void test01 (){
ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-06.xml");
BeanLife beanLife = (BeanLife) context.getBean("beanLife");
System.out.println("测试结果BeanLife:"+beanLife.getName()) ;
beanLife.useBean();
// 关闭容器
((AbstractApplicationContext) context).close();
}
}
- 输出结果
1、设置名称:myBeanLifeOne
2、BeanNameAware..SetName:beanLife
3、初始化之前...beanLife
4、初始化Bean..
5、初始化之后...beanLife
6、BeanNameAware..SetName:myBeanLifeTwo
7、测试结果BeanLife:myBeanLifeOne
8、使用Bean..
9、销毁Bean..
这里梳理Bean的生命周期,过程十分清晰。
======================================================================
Spring 框架基础(04):AOP切面编程概念,几种实现方式演示
一、AOP基础简介
1、切面编程简介
AOP全称:Aspect Oriented Programming,面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。核心作用:可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的复用性和开发效率。AOP提供了取代继承和委托的一种新的方案,而且使用起来更加简洁清晰,是软件开发中的一个热点理念。
2、AOP术语
(1)、通知类型:Advice
前置通知[Before]:目标方法被调用之前;
返回通知[After-returning]:目标方法执行成功之后;
异常通知[After-throwing]:在目标方法抛出异常之后;
后置通知[After]:目标方法完成之后;
环绕通知[Around]:在目标方法执行前后环绕通知;
(2)、连接点:JoinPoint
程序执行的某一个特定位置,如类初始前后,方法的运行前后。
(3)、切点:Pointcut
连接点是指那些在指定策略下可能被拦截到的方法。
(4)、切面:Aspect
切面由切点和通知的结合。
(5)、引入:Introduction
特殊的增强,为类添加一些属性和方法。
(6)、织入:Weaving
将增强添加到目标类的具体连接点上的过程。编译期织入,这要求使用特殊编译器;类装载期织入,这要求使用特殊的类加载器;动态代理织入,在运行期为目标类添加增强生成子类的方式,Spring采用的是动态代理织入,而AspectJ采用编译期织入和类装载期织入。
(7)、代理:Proxy
类被AOP织入后生成一个结果类,它是融合了原类和增强逻辑的代理类。
二、AOP编程实现方式
案例基于如下类进行:
public class Book {
private String bookName ;
private String author ;
}
public interface BookService {
void addBook (Book book) ;
}
public class BookServiceImpl implements BookService {
@Override
public void addBook(Book book) {
System.out.println(book.getBookName());
System.out.println(book.getAuthor());
}
}
1、JDK动态代理
public class BookAopProxyFactory {
public static BookService createService() {
// 目标类
final BookService bookService = new BookServiceImpl() ;
// 切面类
final BookAspect bookAspect = new BookAspect();
/*
* 代理类:将目标类(切入点)和 切面类(通知) 结合
*/
BookService proxyBookService = (BookService) Proxy.newProxyInstance(
BookAopProxyFactory.class.getClassLoader(),
bookService.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 前执行
bookAspect.before();
// 执行目标类的方法
Object obj = method.invoke(bookService, args);
// 后执行
bookAspect.after();
return obj;
}
});
return proxyBookService ;
}
}
2、CgLib字节码增强
采用字节码增强框架cglib,在运行时创建目标类的子类,从而对目标类进行增强。
public class BookAopCgLibFactory {
public static BookService createService() {
// 目标类
final BookService bookService = new BookServiceImpl() ;
// 切面类
final BookAspect bookAspect = new BookAspect();
// 核心代理类
Enhancer enhancer = new Enhancer();
// 确定父类
enhancer.setSuperclass(bookService.getClass());
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object proxy, Method method,
Object[] args,
MethodProxy methodProxy) throws Throwable {
bookAspect.before();
Object obj = method.invoke(bookService, args);
bookAspect.after();
return obj;
}
});
BookServiceImpl proxyService = (BookServiceImpl) enhancer.create();
return proxyService ;
}
}
3、Spring半自动代理
spring 创建代理对象,从spring容器中手动的获取代理对象。
- 配置文件
<!-- 创建目标类 -->
<bean id="bookService" class="com.spring.mvc.service.impl.BookServiceImpl" />
<!-- 创建切面类 -->
<bean id="myAspect" class="com.spring.mvc.config.BookAopSpringHalf" />
<!-- 创建代理类 -->
<bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.spring.mvc.service.BookService" />
<property name="target" ref="bookService" />
<property name="interceptorNames" value="myAspect" />
</bean>
- 切面类
public class BookAopSpringHalf implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Method Before ...");
Object obj = methodInvocation.proceed();
System.out.println("Method After ...");
return obj;
}
}
4、Spring全自动代理
从spring容器获得目标类,如果配置Aop,spring将自动生成代理。
- 配置文件
<!-- 创建目标类 -->
<bean id="bookService" class="com.spring.mvc.service.impl.BookServiceImpl" />
<!-- 创建切面类 -->
<bean id="myAspect" class="com.spring.mvc.config.BookAopSpringHalf" />
<!-- AOP编程配置 -->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.spring.mvc.service.*.*(..))"
id="myPointCut"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/>
</aop:config>
5、综合测试
@Test
public void test1 (){
BookService bookService = BookAopProxyFactory.createService() ;
Book book = new Book() ;
book.setBookName("Spring实战");
book.setAuthor("Craig Walls");
bookService.addBook(book);
}
@Test
public void test2 (){
BookService bookService = BookAopCgLibFactory.createService() ;
Book book = new Book() ;
book.setBookName("MySQL");
book.setAuthor("Baron");
bookService.addBook(book);
}
@Test
public void test3 (){
String xmlPath = "spring-aop-half.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
BookService bookService = (BookService) context.getBean("proxyFactory");
Book book = new Book() ;
book.setBookName("红楼梦");
book.setAuthor("曹雪芹");
bookService.addBook(book);
}
@Test
public void test4 (){
String xmlPath = "spring-aop-all.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
BookService bookService = (BookService) context.getBean("bookService");
Book book = new Book() ;
book.setBookName("西游记");
book.setAuthor("吴承恩");
bookService.addBook(book);
}
三、AspectJ切面编程
1、基础简介
AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持,通过JDK5注解技术,允许直接在类中定义切面,新版本Spring框架,推荐使用AspectJ方式来开发AOP编程。
2、XML配置方式
- 切面类
public class BookAopAspectJ {
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知:" + joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知:" + joinPoint.getSignature().getName() + " , -->" + ret);
}
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕通知前");
Object obj = joinPoint.proceed();
System.out.println("环绕通知前后");
return obj;
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
- 配置文件
<!-- 创建目标类 -->
<bean id="bookService" class="com.spring.mvc.service.impl.BookServiceImpl" />
<!-- 创建切面类 -->
<bean id="myAspect" class="com.spring.mvc.config.BookAopAspectJ" />
<!-- 配置AOP编程 -->
<aop:config>
<aop:aspect ref="myAspect">
<aop:pointcut expression="execution(* com.spring.mvc.service.impl.BookServiceImpl.*(..))" id="myPointCut"/>
<!-- 前置通知-->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
<!-- 后置通知 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
<!-- 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut"/>
<!-- 抛出异常 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
<!-- 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
- 测试方法
@Test
public void test1 (){
String xmlPath = "spring-aop-aspectj-01.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
BookService bookService = (BookService) context.getBean("bookService");
Book book = new Book() ;
book.setBookName("三国演义");
book.setAuthor("罗贯中");
bookService.addBook(book);
}
3、注解扫描方式
- 配置文件
<!-- 开启类注解的扫描 -->
<context:component-scan base-package="com.spring.mvc.service.impl" />
<!-- 确定AOP注解生效 -->
<aop:aspectj-autoproxy />
<!-- 声明切面 -->
<bean id="myAspect" class="com.spring.mvc.config.AuthorAopAspectJ" />
<aop:config>
<aop:aspect ref="myAspect" />
</aop:config>
- 注解切面类
@Component
@Aspect
public class AuthorAopAspectJ {
@Pointcut("execution(* com.spring.mvc.service.impl.AuthorServiceImpl.*(..))")
private void myPointCut(){
}
@Before("execution(* com.spring.mvc.service.impl.AuthorServiceImpl.*(..))")
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知:" + joinPoint.getSignature().getName());
}
@AfterReturning(value="myPointCut()" ,returning="ret")
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知:" +
joinPoint.getSignature().getName() + " , -->" + ret);
}
@Around(value = "myPointCut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕通知前");
Object obj = joinPoint.proceed();
System.out.println("环绕通知前后");
return obj;
}
@AfterThrowing(
value="execution(* com.spring.mvc.service.impl.AuthorServiceImpl.*(..))",
throwing="e")
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
- 测试方法
@Test
public void test2 (){
String xmlPath = "spring-aop-aspectj-02.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
AuthorService authorService = (AuthorService) context.getBean("authorService");
System.out.println("作者:"+authorService.getAuthor());
}
================================================================================
事务管理机制,和实现方式
1、基础描述
Spring
事务管理的本质就是封装了数据库对事务支持的操作,使用JDBC的事务管理机制,就是利用java.sql.Connection
对象完成对事务的提交和回滚。
Connection conn = DriverManager.getConnection();
try {
// 自动提交设置为false
conn.setAutoCommit(false);
// 执行增删改查操作
// 当操作成功后手动提交
conn.commit();
} catch (Exception e) {
// 出现异常,回滚所有操作
conn.rollback();
e.printStackTrace();
} finally {
conn.colse();
}
2、事务常见概念
- 事务
事务是指作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部成功,要么全部不成功。
- 特性:ACID
原子性(Atomicity):事务中的多个操作要么都成功要么都失败
一致性(consistency):事务的执行的前后数据的完整性保持一致
隔离性(isolation):事务执行的过程中,不应该受到其他事务的干扰
持久性(durability):事务一旦结束,数据就持久到数据库
- 隔离问题
脏读:一个事务读到另一个事务没有提交的数据
不可重复读:一个事务前后多次读取相同数据,数据内容不一致,update场景问题
虚读(幻读):一个事务前后多次读取,数据总量不一致,insert场景问题
- 隔离级别
read uncommitted
:
事务可以读取另一个未提交事务的数据。
read committed
:
事务要等另一个事务提交后才能读取数据,解决脏读。
repeatable read
:
在开始读取数据时,事务开启,不再允许修改操作,解决:脏读、不可重复读。
serializable
:
最高事务隔离级别,事务串行化顺序执行,解决脏读、不可重复读、幻读。但是效率低下,耗数据库性能。
3、事务管理API描述
PlatformTransactionManager
平台事务管理器,Spring管理事务,必须使用事务管理器进行事务配置时,核心方法:获取事务,提交事务,回滚事务。
TransactionDefinition
该对象封装事务详情(事务定义、事务属性),例如:隔离级别、是否只读、超时时间 等。
TransactionStatus
用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。Spring底层根据状态进行相应操作。
4、事务案例SQL语句
CREATE TABLE user_account(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
money INT
) ENGINE=InnoDB DEFAULT CHARSET=utf8;;
INSERT INTO user_account(username,money) VALUES('jack','5000');
INSERT INTO user_account(username,money) VALUES('tom','5000');
SELECT * FROM user_account ;
二、编程式事务管理
1、核心配置文件
<!-- 配置事物管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 创建事物模板 -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager" />
</bean>
<!-- 配置转账接口 -->
<bean id="userAccountService"
class="com.spring.mvc.service.impl.UserAccountServiceImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="transactionTemplate" ref="transactionTemplate" />
</bean>
2、演示案例
@Service
public class UserAccountServiceImpl extends JdbcDaoSupport implements UserAccountService {
// 注入事物模板
private TransactionTemplate transactionTemplate ;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void remittance(String remitTer, String receiver, int money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus arg0) {
outMoney(remitTer,money);
// System.out.println(1/0);
innerMoney(receiver,money);
}
});
}
private void outMoney (String remitTer, int money){
String outSql = "update user_account set money = money - ? where username = ?";
this.getJdbcTemplate().update(outSql, money ,remitTer);
}
private void innerMoney (String receiver, int money){
String inSql = "update user_account set money = money + ? where username = ?";
this.getJdbcTemplate().update(inSql, money,receiver);
}
}
三、事务代理工厂
1、核心配置
<!-- 配置转账接口 -->
<bean id="userAccountService01"
class="com.spring.mvc.service.impl.UserAccountServiceImpl01">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 配置事务代理工厂 -->
<bean id="proxyAccountService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces" value="com.spring.mvc.service.UserAccountService01" />
<property name="target" ref="userAccountService01" />
<property name="transactionManager" ref="txManager" />
<property name="transactionAttributes">
<props>
<!-- 默认传播行为、隔离级别 -->
<prop key="remittance">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
<!-- 异常仍然提交事务
<prop key="remittance">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.Exception</prop>
-->
</props>
</property>
</bean>
配置说明:
proxyInterfaces
代理接口target
代理目标类transactionManager
事务管理器transactionAttributes
事务属性(事务详情)prop.key
指定方法使用当前事务配置
事务行为:
PROPAGATION
传播行为ISOLATION
隔离级别readOnly
是否只读-Exception
异常回滚+Exception
异常提交
2、演示案例
@Service
public class UserAccountServiceImpl01 extends JdbcDaoSupport implements UserAccountService01 {
@Override
public void remittance(String remitTer, String receiver, int money) {
outMoney(remitTer,money);
System.out.println(1/0);
innerMoney(receiver,money);
}
private void outMoney (String remitTer, int money){
String outSql = "update user_account set money = money - ? where username = ?";
this.getJdbcTemplate().update(outSql, money ,remitTer);
}
private void innerMoney (String receiver, int money){
String inSql = "update user_account set money = money + ? where username = ?";
this.getJdbcTemplate().update(inSql, money,receiver);
}
}
3、测试代码
public class Tx_Test_02 {
@Test
public void test1 (){
String xmlPath = "spring-jdbc-tx-02.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
UserAccountService01 accountService = (UserAccountService01)
context.getBean("proxyAccountService");
accountService.remittance("jack","tom",1000);
}
}
四、XML配置事务
首先配置事务管理器,然后配置事务属性,最后基于AOP编程配置事务切入点。
1、核心配置
<!-- 配置事物管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置转账接口 -->
<bean id="userAccountService01"
class="com.spring.mvc.service.impl.UserAccountServiceImpl01">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 配置事物详情 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="remittance"
propagation="REQUIRED"
isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 事务切入点,基于AOP编程 -->
<aop:config>
<aop:advisor
advice-ref="txAdvice"
pointcut="execution(* com.spring.mvc.service.UserAccountService01.*(..))"/>
</aop:config>
五、基于事务注解
配置事务管理器,并启动事务注解的支持,在目标类或目标方法添加@Transactional
核心注解即可。
1、核心配置
<!-- 配置事物管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 支持事务注解 -->
<tx:annotation-driven transaction-manager="txManager" />
2、注解用法
注解写在接口方法上,或者接口实现的方法上效果一样。
public interface UserAccountService02 {
@Transactional(rollbackFor = Exception.class)
void remittance(String remitTer, String receiver, int money) ;
}
======================================================================================
Mvc架构模式简介,执行流程详解
一、SpringMvc框架简介
1、Mvc设计理念
MVC是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个组件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,MVC分层有助于管理和架构复杂的应用程序
- M:代表模型
Model
模型就是数据,应用程序的核心。
- V:代表视图
View
回显数据的界面,例如JSP就是用来展示模型中的数据。
- C:代表控制器
Controller
控制器的作用就是根据入参,把不同的响应数据(Model
),显示在不同的视图(View
)上。
2、SpringMvc简介
- 框架描述
SpringMVC
是一种基于Java
实现的MVC
设计模式的请求驱动类型的轻量级Web
框架,出自Spring
框架全家桶,与Spring
框架无缝整合,使用了MVC
架构模式的思想,将Web
层进行职责解耦。
- 框架优点
结构松散,几乎可以在SpringMVC
中使用各类视图,各个模块分离而且耦合度非常低,且易于扩展。与Spring
无缝集成,且简单,灵活,容易上手。
二、SpringMvc执行流程
1、流程图解
2、步骤描述
(1)、发起请求到前端控制器DispatcherServlet
;
(2)、前端控制器请求HandlerMapping
查找,Handler
可以根据xml
配置、注解进行查找;
(3)、处理器映射器HandlerMapping
向前端控制器返回Handler
;
(4)、前端控制器调用处理器适配器去执行Handler
;
(5)、处理器适配器去执行Handler
;
(6)、Handler
执行完成给适配器返回ModelAndView
;
(7)、处理器适配器向前端控制器返回ModelAndView
,ModelAndView
是springmvc
框架的一个底层对象,包括Model
和view
;
(8)、前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图 ;
(9)、视图解析器向前端控制器返回View
;
(10)、前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView
对象中)填充到request
域中;
(11)、前端控制器向用户响应结果 ;
3、核心组件
- 前端控制器
DispatcherServlet
:请求离开浏览器后,最先到达的就是DispatcherServlet,是整个流程控制的中心,作用接收请求,响应结果,相当于转发器,中央处理器。减少各个组件之间的耦合度。
- 处理器映射器
HandlerMapping
:根据请求的url路由到指定接口,用户请求找到Handler处理器,springmvc提供不同类型映射器,例如:Xml配置方式,注解方式等。
- 处理器适配器
HandlerAdapter
:按照特定规则去执行Handler,SpringMvc支持多种处理器,各种处理器中的处理方法各不相同,为了解决适应多种处理器,就出现了处理器适配器。
- 处理器
Handler
:处理用户请求,涉及具体业务逻辑,需要程序员根据业务需求开发。编写Handler时按照HandlerAdapter的规则开发,这样适配器才可以正确执行Handler。
- 视图解析器
ViewResolver
:负责将请求的响应结果生成View,根据逻辑视图名解析成物理视图名,就是具体页面地址,生成View视图对象,对View进行渲染,通过页面展示给用户。
- 视图
View
:SpringMvc框架提供很多的View视图类型的支持,包括:jsp、freemarker、pdf等。通过页面标签或页面模版解析模型数据回显到页面,需要根据业务开发具体页面。
三、整合Spring框架配置
1、spring-mvc配置
<!-- 扫描文件 -->
<context:component-scan base-package="com.spring.mvc.controller" />
<!-- MVC默认的注解映射的方式 -->
<mvc:annotation-driven />
<mvc:default-servlet-handler/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/" />
<property name="suffix" value=".jsp" />
</bean>
2、Web.xml配置
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3、测试接口
@Controller
public class HelloController {
@RequestMapping("/getInfo")
public @ResponseBody String getInfo (String name){
return name ;
}
}
4、常用注解说明
@Controller
标记一个类是Handler,也就是开发的Controller,然后使用@RequestMapping或其他相关注解(@GetMapping、@PostMapping、@PutMapping、@DeleteMapping),用来关联请求和Controller方法之间的映射关系,这样的Controller 就可以被请求访问。
@RequestMapping
处理请求地址映射的注解,可作用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以类上标注地址作为父路径。
@requestParam
主要用于在SpringMvc框架的控制层获取参数,三个常用参数:defaultValue表示设置默认值,required 通过boolean设置是否是必须要传入的参数,value值表示传入的参数名称。
@RequestBody
接收请求体中传递给后端的Json字符串数据的,GET方式无请求体,所以使用@RequestBody接收数据时,不能使用GET方式提交数据,需要用POST方式进行提交。
@ResponseBody
该注解用于方法的返回对象,可以通过配置转换器为指定数据响应格式,如果希望返回的数据不是View试图页面,而是指定数据格式的时候使用,例如:Json、Xml等。
@Autowired
按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
@Resource
按照ByName自动注入,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。
@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
四、常见参数映射
1、普通映射
@RequestMapping("/getSum")
public Integer getSum (int a,int b){
return a+b ;
}
测试:
http://localhost:6003/getSum?a=1&b=2
传参名称和方法参数保持一致。
2、指定参数名
@RequestMapping("/getInfo")
public String getInfo (@RequestParam("name") String var1,
@RequestParam("say") String var2){
return var1+":"+var2 ;
}
测试:
http://localhost:6003/getInfo?name=cica&say=hello
传参名和 @RequestParam 指定的参数名要对应。
3、数组参数
@GetMapping("/getArray")
public String getArray (String[] ids){
return ids[0]+"-"+ids[1] ;
}
测试:
http://localhost:6003/getArray?ids=2&ids=3
传递并解析数组类型的参数格式。
4、Map参数
@RequestMapping("/getMap")
public String getMap (@RequestParam Map<String,String> paramMap){
return paramMap.get("name") ;
}
测试:
http://localhost:6003/getCityEntity?province=浙江&name=杭州
这里以Post方式将相关参数传递CityEntity实体对象中。
5、包装参数
@PostMapping("/getCityEntity")
public CityEntity getCityEntity (CityEntity cityEntity){
return cityEntity ;
}
测试:
http://localhost:6003/getCityEntity?province=浙江&name=杭州
这里以Post方式将相关参数传递CityEntity实体对象中。
6、Rest风格参数
@GetMapping("/getId/{id}")
public String getId (@PathVariable("id") Integer id){
return "id="+id ;
}
测试:
http://localhost:6003/getId/1
RestFul 风格参数映射。
GitHub·地址 https://github.com/cicadasmile/spring-mvc-parent
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战