前言
刚学完ssm框架,重新回顾一下spring框架基于xm和注解l的配置,如果有不对的地方,欢迎在下方留言,希望能巩固自己的对框架的理解和能帮助到大家,谢谢!
spring框架两大核心思想
IOC 控制反转
IOC,即(Inversion of Control),也就是控制反转。用自己(听得网课哈哈)的理解就是将创建对象的权利交给spring的IOC容器,让IOC容器来实现对象的实例化,作用是达到了降低程序间的耦合。扯到IOC,有一个概念,依赖注入,会在后面进行讲解!
AOP 面向切面编程
AOP,即(Aspect Oriented Programming),面向切面编程。假如有一个场景:当你自己写个小项目的时候,这个时候发现几十个对数据库进行操作的方法都没有进行事务的控制,这下好了,你就得一个方法一个方法的进行try/catch/finally,那么你就会难受了。而AOP的思想很好的解决了这种耦合。
基于xml的IOC配置过程(IDEA)
首先创建maven工程然后,在pom.xml文件下配置dependency
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
申明一下如果引入坐标慢的话可以改成阿里云的镜像,方法可以百度,教程很多!
然后新建一个实体类
/**用户信息实体类
* @author Cocowwy
* @create 2020-04-04-23:56
*/
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
然后再在main/resources目录下面写配置文件 首先先写上这块
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
因为我们刚刚写了一个实体类,既然想利用IOC来创建实例,那么进行配置
<bean id="user" class="cn.hncu.domain.User"></bean>
id是自己命名的,不过在代码中想创建实例对象就需要这个id来做唯一标识,class属性是该类的全限定类名。
类有了,配置文件有了,那代码中怎么通过读取配置文件来得到实例对象呢??
在JAVA代码中:
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));//控制台输出:cn.hncu.domain.User@3ffc5af1
}
}
打印出地址说明对象创建成功了,这就是控制反转,将new对象的控制权力转交给spring容器
那么怎么对对象进行赋值呢?
方法一:通过构造方法
<bean id="user" class="cn.hncu.domain.User">
<constructor-arg index="0" value="王小胖"/>
<constructor-arg index="1" value="20"/>
</bean>
当我们对实体类添加上构造方法后(alt+ins生成构造函数),idea会提示配置文件需要新增的标签constructor-arg,该标签index表示第几个参数,注意下标从0开始,value值是需要赋的值。
接下来重写在实体类中重写下tosSring()方法看看结果!
//构造函数
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
//重写toString
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
测试:
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));//控制台输出:User{name='王小胖', age=20}
}
}
可以看到赋值成功了!
方法二:通过属性注入(更常用)
先将上一个方法的构造器删除
进行如下配置
<bean id="user" class="cn.hncu.domain.User">
<property name="name" value="王大胖"></property>
<property name="age" value="21"></property>
</bean>
property标签的name属性指的是实体类的属性名,value为赋的值。
测试:
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));//控制台输出:User{name='王大胖', age=21}
}
}
现在我们知道了基本树属性的注入,接下来讲讲特殊属性,如属性为集合,Map,数组等,我们怎么给属性赋值呢?
首先是List集合
首先修改下实体类,通过getset方式注入
/**
* 用户信息实体类
*
* @author Cocowwy
* @create 2020-04-04-23:56
*/
public class User {
private String name;
private Integer age;
private List<String> frinds;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", frinds=" + frinds +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<String> getFrinds() {
return frinds;
}
public void setFrinds(List<String> frinds) {
this.frinds = frinds;
}
}
然后bean.xml配置如下:
<bean id="user" class="cn.hncu.domain.User">
<property name="name" value="王小胖"></property>
<property name="age" value="20"></property>
<property name="frinds" >
<array>
<value>王一胖</value>
<value>王二胖</value>
<value>王三胖</value>
</array>
</property>
</bean>
可以看到property标签的name值为List的名称,property包含了arry标签,每一个value对应的是集合里的每一个值
运行结果如下:
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));//User{name='王小胖', age=20, frinds=[王一胖, 王二胖, 王三胖]}
}
}
数组和List步骤是一样的,就不进行演示了!
接下来是Map集合
给实体添加属性 以及geiset方法,重写下toString
package cn.hncu.domain;
import java.util.List;
import java.util.Map;
/**
* 用户信息实体类
*
* @author Cocowwy
* @create 2020-04-04-23:56
*/
public class User {
private String name;
private Integer age;
private List<String> frinds;
private Map<String,String> thingColor;
public Map<String, String> getThingColor() {
return thingColor;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", frinds=" + frinds +
", thingColor=" + thingColor +
'}';
}
public void setThingColor(Map<String, String> thingColor) {
this.thingColor = thingColor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<String> getFrinds() {
return frinds;
}
public void setFrinds(List<String> frinds) {
this.frinds = frinds;
}
}
接着bean.xml配置如下
<bean id="user" class="cn.hncu.domain.User">
<property name="name" value="王小胖"></property>
<property name="age" value="20"></property>
<property name="frinds" >
<array>
<value>王一胖</value>
<value>王二胖</value>
<value>王三胖</value>
</array>
</property>
<property name="thingColor">
<map>
<entry key="上衣" value="红色"></entry>
<entry key="裤子" value="蓝色"></entry>
<entry key="头发" value="黑色"></entry>
</map>
</property>
</bean>
可以看name为map集合的property 标签将map标签包了起来,map里面有entry标签,代表着键值对,很好理解!
测试:
package cn.hncu;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));//User{name='王小胖', age=20, frinds=[王一胖, 王二胖, 王三胖], thingColor={上衣=红色, 裤子=蓝色, 头发=黑色}}
}
}
properties呢?
同样的添加getset方法和重写toString
private Properties properties;
bean.xml新增配置
<property name="properties">
<props>
<prop key="体重">140</prop>
</props>
</property>
测试:
package cn.hncu;
import cn.hncu.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));
// User {name='王小胖', age=20, frinds=[王一胖, 王二胖, 王三胖], thingColor={上衣=红色, 裤子=蓝色, 头发=黑色}, properties={体重=140}}
}
}
如果是集合里面需要的其他的引用类型呢???
首先先建立两个实体 可以看到List集合的泛型类型是Account
package cn.hncu.domain;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* 用户信息实体类
*
* @author Cocowwy
* @create 2020-04-04-23:56
*/
public class User {
private String name;
private Integer age;
private List<Account> accounts;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", accounts=" + accounts +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
}
Account实体
package cn.hncu.domain;
/**
* @author Cocowwy
* @create 2020-04-04-11:32
*/
public class Account {
private String username;
private String money;
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", money='" + money + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getMoney() {
return money;
}
public void setMoney(String money) {
this.money = money;
}
}
接下来怎么配置呢,这块地方理解清楚了不管是Map好,数组好,任何地方需要引入实体就都是这么做的!
bean.xml配置如下:
<bean id="account" class="cn.hncu.domain.Account">
<property name="username" value="王小胖工行卡"></property>
<property name="money" value="300000"></property>
</bean>
<bean id="user" class="cn.hncu.domain.User">
<property name="name" value="王小胖"></property>
<property name="age" value="20"></property>
<property name="accounts">
<list>
<ref bean="account"></ref>
</list>
</property>
</bean>
首先因为新建了一个实体,我们自然需要将它注入到IOC容器,所以先配置,然后可以看到bean里面property有个ref属性?对就是它,ref引用到的bean就是我们刚刚注入IOC容器的id=account的bean,ref就是通过id来找到的。举一反三,我们不难得出的是,我们就是通过将value属性替换成ref属性来引用IOC容器里面的bean。
测试:
/**
* @author Cocowwy
* @create 2020-04-04-0:09
*/
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//返回结果是obj类型,记得强转!
System.out.println(ac.getBean("user"));
//User{name='王小胖', age=20, accounts=[Account{username='王小胖工行卡', money='300000'}]}
}
}
以上就是给实体赋值的方式,也就是我们的DI,依赖注入,这就是spring基于XML的IOC配置,讲的比较粗糙,讲的是比较简单的配置!
基于xml的AOP配置过程(IDEA)
spring中基于xml的AOP配置
1.把通知bean也交给spring来管理
2.使用aop:config标签表明开始AOP的配置
3.使用aop:aspect标签表明开始配置切面
id属性:给切面提供唯一标志
ref属性:是指定通知类的bean的id
4.在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在的实例会让pringLog方法在切入点方法执行之前执行,所以前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法时前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名…类名.方法名 (参数列表)
标准写法: public void com.hncu.service.impl.AccountServiceAOP.saveAccount()
访问修饰符可以省略:void com.hncu.service.impl.AccountServiceAOP.saveAccount()
返回值可以使用通配符:表示任意返回值:* com.hncu.service.impl.AccountServiceAOP.saveAccount()
包名可以使用通配符表示任意包,表示任意包,但是有几级包,就需要写几个*.:* ...…AccountServiceAOP.saveAccount()
包名还可以使用…表示当前包及其子包:* …AccountServiceAOP.saveAccount()
类名和方法名都可以使用来实现通配:* ….()
参数列表:
可以直接写数据类型
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但必须有参数
可以使用…表示有无参数均可,有参数可以使任意类型
全统配写法: ….(…)
实际开发中切入点表达式的通常写法
切刀业务层实现类下的所有方法
* com.hncu.service.impl..*(…)
依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
AOP切面的配置
!-- 配置AOP-->
<aop:config>
<!--配置切入点表达式 id:用于指定表达式的唯一表示 expression:用于指定表达式内容
此标签卸写在aop:aspect标签内部只能当前切面使用。
它还可以写在aop:aspect外面(标签必须在切面你之前!!!!!!)-->
<aop:pointcut id="pt1" expression="execution(* com.hncu.service.impl.*.*(..))"/>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<!-- 前置通知,在切入点执行方法之前执行-->
<aop:before method="beforeprintLog" pointcut-ref="pt1"></aop:before>
<!-- 后置通知,在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
<aop:after-returning method="afterReturnprintLog" pointcut-ref="pt1"></aop:after-returning>
<!-- 异常通知,在切入点方法执行异常之后执行,它和后置通知永远只能执行一个-->
<aop:after-throwing method="afterThrowingprintLog" pointcut-ref="pt1"></aop:after-throwing>
<!-- 最终通知,无论切入点方法是否正常执行它都会在其后面执行-->
<aop:after method="afterprintLog" pointcut-ref="pt1"></aop:after>
<!-- 配置环绕通知 详细的注释请看Logger类中-->
<aop:around method="arroundPrintLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
Logger类。
package com.hncu.utils;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @author Cocowwy
* @create 2020-04-04-11:23
* 用于记录日志的工具类,它里面提供了公共代码
*/
public class Logger {
/**
* 用于打印日志,计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
* 前置通知
*/
public void beforeprintLog() {
System.out.println("前置通知Logger类中的beforeprintLog方法开始记录日志了。。。");
}
/**
* 后置通知
*/
public void afterReturnprintLog() {
System.out.println("后置通知Logger类中的afterReturnprintLog方法开始记录日志了。。。");
}
/**
* 异常通知
*/
public void afterThrowingprintLog() {
System.out.println("异常通知Logger类中afterThrowingprintLog方法开始记录日志了。。。");
}
/**
* 最终通知
*/
public void afterprintLog() {
System.out.println("最终通知Logger类中的afterprintLog方法开始记录日志了。。。");
}
/**
* 环绕通知
* 问题:当我们配置了环绕通知之后,切入点方法没有执行,通知方法执行了
* 分析:通过对比动态代理中的环绕通知代码,发现动态代理中环绕通知有明确的切入点方法调用
* 而我们的代码中没有,
* 解决:spring框架为我们提供了一个接口:ProceedingJoinPoint该接口有一个方法proceed(),
* 此方法就相当于明确调用切入点方法
* 该接口可以作为环绕通知的方法参数,在程序执行时spring框架会为我们提供该接口的实现类供我们使用
* <p>
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方式何时执行的方式
*/
public Object arroundPrintLog(ProceedingJoinPoint pjp) {
Object rtvalue = null;
try {
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("环绕前置通知");
rtvalue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("环绕后置通知");
return rtvalue;
} catch (Throwable throwable) {
System.out.println("环绕异常通知");
throw new RuntimeException(throwable);
} finally {
System.out.println("环绕最终通知");
}
}
}
以上就是基于XML的配置,稍后会进行spring基于注解的配置!