Java中Spring框架(初学者)
spring框架
(1)对创建对象的集中处理,因为在实际开发中创建的对象十分之多,因此有spring框架管理这些对象以及它们之间的依赖关系会十分方便。
(2)Spring具有控制反转(IoC)和面向切面(AOP)两大核心。面向切面编程更是大大提高开发效率.
(3)spring框架简单轻便,而且spring框架还可以融合其它的框架,大大提高它的实用性。
一、spring框架的搭建
1.搭建步骤
1.创建一个maven项目,然后在pom配置文件中引入spring依赖
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
2.创建spring配置文件:
2.spring框架的简单使用示例
1.创建一个简单的类:
public class Team {
private String name;
private String location;
private int id;
public Team() {
System.out.println("Team的默认方法被调用:name="+name+",location="+location+",id="+id);
}
public Team(String name, String location, int id) {
this.name = name;
this.location = location;
this.id = id;
System.out.println("Team的带参数的构造方法被调用:name="+name+",location="+location+",id="+id);
}
public String toString(){
return "name="+name+",location="+location+",id="+id;
}
}
2.在spring配置文件中用bean标签编写要创建的对象:
<?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">
<!--id是对象的引用名,class里是填写创建对象的类(包名+类名 spring底层是利用反射机制创建对象)-->
<bean id="team" class="com.huilong.test.Team" ></bean>
</beans>
3.创建启动spring容器,可以通过getBean方法获取容器中的对象:
@Test
public void Test01(){
//1.创建spring容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//2.根据id获取容器中的对象,然后强转为原来对应的类对象
Team team1 = (Team) applicationContext.getBean("team1");
System.out.println(team1.toString());
}
结果:
测试示例成功,环境搭建成功
二、Spring核心之IoC控制反转
1.概念:
IoC 是指在程序开发中,实例的创建不再由调用者管理,而是由 Spring 容器创建。Spring 容器会负责控制程序之间的关系,而不是由程序代码直接控制,因此,控制权由程序代码转移到了 Spring 容器中,控制权发生了反转,这就是 Spring 的 IoC 思想。
2.spring容器
spring提供了两种获取容器的方式:分别为 BeanFactory 和 ApplicationContext.两者创建对象的生命周期不同。
1.ApplicationContext:
(1)ClassPathXmlApplicationContext(指定项目内的配置文件)
(2)FileSystemXmlApplicationContext(指定项目内外的配置文件,用全路径)
2.BeanFactory :
(1)XmlBeanFactory(指定项目内外的配置文件,用全路径)
@Test
public void Test(){
String springConfig="application.xml";
//1.ApplicationContext
//方法一:ClassPathXmlApplicationContext(指定项目内的配置文件)
//创建完容器对象,配置文件的所有的scope="singleton"(这是默认属性)的bean对象全部创建
ApplicationContext applicationContext1=new ClassPathXmlApplicationContext(springConfig);
//方法二:FileSystemXmlApplicationContext(指定项目外的配置文件)
//创建完容器对象,配置文件的所有的scope="singleton"(这是默认属性)的bean对象全部创建
ApplicationContext applicationContext2 = new FileSystemXmlApplicationContext("D:/idea_code/MySpring/spring01/src/main/resources/application.xml");
//2.BeanFactory
//调用getBean(id)方法才会创建指定对象
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("D:/idea_code/MySpring/spring01/src/main/resources/application.xml"));
}
3.spring创建对象的三种方式
(1)调用有参或无参构造器
<!--1.1调用无参构造方法-->
<bean id="team01" class="com.huilong.test.Team"></bean>
<!--1.2调用带参数构造方法-->
<!--指定属性名填入-->
<bean id="team02" class="com.huilong.test.Team">
<constructor-arg name="id" value="1001"></constructor-arg>
<constructor-arg name="name" value="男人"></constructor-arg>
<constructor-arg name="location" value="惠州"></constructor-arg>
</bean>
<!--按照构造器参数的位置填入-->
<bean id="team03" class="com.huilong.test.Team">
<constructor-arg index="2" value="1001"></constructor-arg>
<constructor-arg index="1" value="男人"></constructor-arg>
<constructor-arg index="0" value="惠州"></constructor-arg>
</bean>
(2)用工厂静态方法创建对象
public class MyFactory {
private Team team;
//静态方法
public static Team StaticTeam(){
return new Team("静态","asdas",1300);
}
//实例方法
public Team FactoryTeam(){
return new Team("实例方法","asdas",1303);
}
public MyFactory() {
}
public MyFactory(Team team) {
this.team = team;
}
<!--2用工厂静态方法创建对象-->
<bean id="team04" class="com.huilong.test.MyFactory" factory-method="StaticTeam"></bean>
(3)用实例方法创建对象
<!--3用实例方法创建对象
1.首先创建实例化bean对象
2.调用实例化对象的实例方法-->
<bean id="factory" class="com.huilong.test.MyFactory"></bean>
<bean id="team05" factory-bean="factory" factory-method="FactoryTeam"></bean>
4.给类对象的属性赋值
通过有参构造方法注入在上面
public class MyFactory {
private Team team;
public void setTeam(Team team) {
this.team = team;
}
public Team getTeam() {
return team;
}
}
(1)通过set方法注入
给MyFactory中的属性team赋值
<!--先创建值对象-->
<bean id="team01" class="com.huilong.test.Team"></bean>
<!--2.通过set方法注入,-->
<bean id="teamFactory" class="com.huilong.test.MyFactory">
<property name="team" ref="team01"></property>
</bean>
(2)自动注入byName和byType
这里都要求值对象先创建
注意:
1.byName要求值对象的id值与属性名相同
2.byType是将一个与属性类型相同的bean对象注入,没有找到匹配不会报错,但与属性类型相同的bean对象有两个及以上的话,就会报错
<!--先创建值对象-->
<bean id="team" class="com.huilong.test.Team"></bean>
<!--3.1通过id与属性名相同注入-->
<bean id="teamFactory1" class="com.huilong.test.MyFactory" autowire="byName"></bean>
<!--3.1通过bean类型与属性类型相同注入-->
<bean id="teamFactory2" class="com.huilong.test.MyFactory" autowire="byType"></bean>
5.通过注解实现Ioc
实现流程是给要创建对象的类加上注解,然后在spring配置文件中添加要扫描的包,在创建spring容器时,程序会扫描指定包中的类是否含有对应的注解,有则在spring容器中创建对象
(1)修改spring配置文件
引入context标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
修改完后,在beans标签里用context标签添加要扫描的包,包下所有类或子包的类都会被扫描
<!--将要扫描的包添加到base-package属性里-->
<!--这段指com.huilong.test包下所有类或子包的类都会被扫描-->
<context:component-scan base-package="com.huilong.test"></context:component-scan>
如果要扫描多个包,base-package属性里可以添加多个包,可以用空格、逗号、分号隔开如:
<context:component-scan base-package="com.huilong.test,com.huilong.ioc"></context:component-scan>
(2)给类加上注解
一、有四种类注解可以加,它们有着不一样的含义,但都可以被扫描出来创建对象:
1.@Component:通用
2.@Repository : 用于dao实现类的的注解
3.@Service: 用户service实现类的注解
4.@Controller: 用于controller实现类的注解
二、给对象的属性赋值:
1.用@Value方式(适用于基本数据类型和字符串,如数字,字符串),该注解也可以加在属性的set方法上
2.对象类型的属性赋值在下方
下面就是通过注解实现Team类对象的创建:
注解加在类的开头
//@Component(value="team") value对应bean中的id
//@Component() 可以不填,默认为类名(开头变为小写,这里默认为team)
@Component("team") //value可以省略
public class Team {
@Value("huilong")
private String name;
@Value("广东惠州")
private String location;
@Value("123456")
private int id;
public Team() {
System.out.println("Team的默认方法被调用:name="+name+",location="+location+",id="+id);
}
public Team(String name, String location, int id) {
this.name = name;
this.location = location;
this.id = id;
System.out.println("Team的带参数的构造方法被调用:name="+name+",location="+location+",id="+id);
}
public String toString(){
return "name="+name+",location="+location+",id="+id;
}
}
2.有两种方式可以给对象类型属性赋值:byName和byType,用注解实现有:
Ⅰ.byName:
(1)@Resource(name=“bean对象id”)jdk提供的注解
(2)@Qualifier(name)
Ⅱ. byType:
(1)@Autowired(required=true),在容器中寻找与属性类型相同的bean对象注入,括号里required属性默认为true,可不填。为true时,在容器中找不到匹配的bean对象就会报错,终止程序运行;为false时,就会忽略找不到匹配项的情况,程序继续运行。
(2)@Resource(type=“类”)
@Repository
public class MyFactory {
//@Resource(name = "team")
@Resource(type=Team.class)
//@Qualifier("team") 按名字查找相同的bean的id
//@Autowired
private Team team;
public static Team StaticTeam(){
return new Team("静态","asdas",1300);
}
public Team FactoryTeam(){
return new Team("实例方法","asdas",1303);
}
public MyFactory() {
}
public MyFactory(Team team) {
this.team = team;
}
}
注意:@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
如果找不到Resource注解,在maven配置文件引入,注意要jdk1.6版本以上:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
三、Spring核心之AOP
AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP的作用:不修改源码的情况下,程序运行期间对方法进行功能增强
好处:
1、减少代码的重复,提高开发效率,便于维护。
2、专注核心业务的开发。核心业务和服务性代码混合在一起开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中
1.动态代理的理解
如果大家想理解动态代理的原理,可以了解静态代理的过程,动态代理就是静态代理的延申。我理解的动态代理就是将平时哪些很多重复一样的代码另外封装起来,如事务开始,日志写入,连接数据库等重复性的代码,在程序开始时组装起来,这样便于维护。它不同于静态代理的是我们不用自己编写很多代理类,动态代理源码已经有封装好的方法,我们可以用注解、修改spring配置文件的方式更简便、直观地实现动态代理。
2.用注解实现动态代理
(1)修改配置文件
在spring配置文件修改为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标签中引入Aop和context约束-->
<!--依然要扫描并创建对象-->
<context:component-scan base-package="com.huilong"/>
<!--这里就是要扫描切面类(有@Aspect注解的类),proxy-target-class="true"代表开启切面编程服务-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
再向maven的pom文件里引入相应依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
(2)定义切面类
1.切面类就是封装那些常用重复性的代码,要用@Aspect注解标明,让程序扫描。切面类中的方法代码最终是与核心业务代码组装结合运行的。
2.切面类方法分为五种:
(1)前置通知(用@Before标记,在核心业务方法之前执行)
(2)后置通知(用@AfterReturning标记,在核心业务方法之后执行)
(3)环绕通知(用@Around标记 在核心业务方法前后执行)
(4)异常通知(用@AfterThrowing标记,在核心业务方法出现异常时执行)
(5)最终通知(用@After标记,在核心业务方法之前执行,必定执行,相当于finally,有异常也执行)
3.最后这些切面类方法要与哪些核心业务类的方法组合,我们得告知程序要拦截哪些方法:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中就是方法的签名。
表达式中斜体文字表示可省略部分,加粗部分不可省略,各部分间用空格分开。在其中可以使用以下符号:
符号 | 意义 |
---|---|
* | 0-多个任意字符 |
.. | 用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径 |
+ | 用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类 |
举例:
execution(* com.kkb.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.kkb.service.*.update*(..))
指定切入点为:定义在 service 包里的任意类的以"update"为开头的方法。
execution(* com.kkb.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟 “*”,表示包、子包下的所有类。
execution(* com.kkb.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类, 则为该类及其子类中的任意方法。
切面类定义实例:
@Component //依然要在spring容器中创建实例
@Aspect //标明是切面类,让程序识别
public class Aop {
//这里拦截的是com.huilong.test包下所有类所有方法,在方法之前执行
@Before("execution(* com.huilong.test..*.*(..))")
public void before(JoinPoint jp){ //这里的jp对象指拦截到的方法
String name = jp.getSignature().getName();//获取方法名称
System.out.println(name);
System.out.println("这是前置通知————————");
}
/**
* AfterReturning 注解声明后置通知
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果 */
@AfterReturning(value = "execution(* com.huilong.test..*.*(..))",returning = "result")
//这里参数名要与上面returning 里的值保持一致
public void after(Object result){
System.out.println("这是后置通知————————"+result);
}
@Around(value = "execution(* com.huilong.test..*.*(..))")
public Object round(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("这是环绕通知————————");
Object process=proceedingJoinPoint.proceed(); //拦截到的方法本身开始执行
System.out.println("这是环绕通知————————");
return process;
}
/**
* AfterThrowing 注解声明异常通知方法
* value: 表示切入点表达式
* throwing属性表示 出现的异常 */
@AfterThrowing(value = "execution(* com.huilong.test..*.*(..))",throwing="ex")
//这里Exception参数名要与上面throwing里的值保持一致
public void exception(JoinPoint joinPoint,Exception ex){
System.out.println("这是异常通知————————");
System.out.println(joinPoint.getSignature()+"出现异常");
}
//拦截com.huilong.test包或子包下所有类的以update为开头的方法
@After(value ="execution(* com.huilong.test..*.update*(..))")
public void myFinally(){
System.out.println("这是最终通知————————");
}
}
这里切入点太多,可以列出来作为一个变量使用:
public class Aop {
@Pointcut("execution(* com.huilong.test..*.*(..))")
private void pointCut(){
}
@Before("pointCut()")
public void before(){
System.out.println("这是前置通知————————");
}
@AfterReturning(value = "pointCut()",returning = "result")
//这里参数名要与上面returning 里的值保持一致
public void after(Object result){
System.out.println("这是后置通知————————"+result);
}
(3)核心业务代码及运行
核心业务代码:
@Service
public class TeamService implements IService{
@Override
public void add() {
System.out.println("添加成功——————————");
}
@Override
public boolean update(int id) {
System.out.println("更新成功————————");
if (id>45){
return true;
}
return false;
}
}
进行测试:
@Test
public void Test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
TeamService teamService = (TeamService) context.getBean("teamService");
teamService.add();
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
System.out.println(teamService.update(56));
}
结果:
3.用xml实现动态代理(即在spring文件上实现)
重新定义一个切面类:
@Component
@Aspect //这里依然要注明
public class Aspect1 {
public void before(JoinPoint jp){
System.out.println("这是前置通知————————");
}
public void after(Object result){
System.out.println("这是后置通知————————"+result);
}
public Object round(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("这是环绕通知————————");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("这是环绕通知————————");
return proceed;
}
public void exception(JoinPoint joinPoint,Exception ex){
System.out.println("这是异常通知————————");
System.out.println(joinPoint.getSignature()+"出现异常");
}
public void myFinally(){
System.out.println("这是最终通知————————");
}
}
在spring文件上编写:
<context:component-scan base-package="com.huilong"/>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<aop:config><!--声明切入点的表达式,可以声明多个-->
<aop:pointcut id="pt1" expression="execution(* com.huilong.test..*.*(..))"/>
<aop:pointcut id="pt2" expression="execution(* com.huilong.test..*.update*(..))"/>
<aop:aspect ref="aspect1">
<aop:before method="before" pointcut-ref="pt1"></aop:before>
<aop:after-returning method="after" pointcut-ref="pt2" returning="result"></aop:after-returning>
<aop:after-throwing method="exception" pointcut-ref="pt1" throwing="ex"></aop:after-throwing>
<aop:after method="myFinally" pointcut-ref="pt1"></aop:after>
<aop:around method="round" pointcut-ref="pt2"></aop:around>
</aop:aspect>
</aop:config>