Spring基础学习

IOC-xml

入门

程序耦合问题:程序间的依赖和被依赖的关系。

编译期依赖和运行期依赖,尽量解决运行期依赖。

ioc控制反转

ioc是一种编程思想。由主动编程变为被动接收。ioc的实现是通过ioc容器来实现的。ioc容器–BeanFactory。

控制权发生反转的过程就是控制反转,好处是能够降低程序间的依赖关系。解决程序耦合问题。

控制的内容:指谁来控制对象的创建;传统的应用程序对象的创建是由程序本身控制的。使用spring后,是由spring来创建对象的。
反转:正转指程序来创建对象,反转指程序本身不去创建对象,而变为被动的接收对象。
总结:以前对象是由程序本身来创建的,使用spring后,程序变为被动的接收spring创建好的对象。

spring配置文件

1、bean配置

1
2
3
<bean id="h1" name="h1,h2,h3,h4" class="cn.temp.bean.Hello">
<property name="name" value="zhangSan" />
</bean>

id是bean的标识符,是唯一的,如果没有配置id,name为默认标识符。
如果配置了id,又配置了name,那么name是别名。
name可以设置多个别名,分隔符可以是空格、逗号、分号。
class是bean的全限定名=包名+类名。
如果不配置id和name,那么可以根据applicationContext.getBean(Class)获取对象。

2、团队协作开发
通过<import resource="path.xml"/>来引入外部xml文件。

bean创建的两种规则

ApplicationContext BeanFactory
提供的是一种立即加载思想来创建bean对象,只要一解析完配置文件,就立马创建bean对象 提供是一种延迟加载思想来创建bean,bean对象什么时候用什么时候创建(已过时,java标准不推荐用)

bean.xml:

1
2
<bean id="customerService" class="cn.demo.service.impl.CustomerServiceImpl"></bean>
<bean id="customerDao" class="cn.demo.dao.impl.ICustomerDaoImpl"></bean>

ApplicationContext

1
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

BeanFactory

1
2
3
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
factory.getBean("customerDao");

bean实例化的三种方式

第一种方式:调用默认无参构造函数创建。默认情况下,如果类中没有默认无参构造方法,则创建失败,会抛出异常。(这种方式用的最多)

第二种方式:使用静态工厂中方法创建对象。需要使用bean标签的Factory-method属性,指定静态工厂中创建对象的方法。

bean.xml:

1
2
<!--使用StaticFactory工厂的getCustomerService方法来创建对象存入容器,用id为staticFactory来取 -->
<bean id="staticFactory" class="cn.demo.factory.StaticFactory" factory-method="getCustomerService"></bean>

cn.demo.factory.StaticFactory.java:

1
2
3
4
5
6
public class StaticFactory {

public static ICustomerService getCustomerService() {
return new CustomerServiceImpl();
}
}

第三种方式:使用实例工厂中的方法创建。
bean.xml

1
2
3
<!--先创建一个工厂对象,在调用工厂对象里的getCustomerService方法来实例化instanceCustomer-->
<bean id="instanceFactory" class="cn.demo.factory.InstanceFactory"></bean>
<bean id="instanceCustomerService" factory-bean="instanceFactory" factory-method="getCustomerService"></bean>

cn.demo.factory.InstanceFactory.java

1
2
3
4
5
6
7
8
9
public class InstanceFactory {
public InstanceFactory() {
System.out.println("instanceFactory..." + this.toString());
}

public ICustomerService getCustomerService() {
return new CustomerServiceImpl();
}
}

bean的作用域

可以通过配置的方式来调整作用范围
配置的属性:bean标签的scope属性。

单例(Sinleton):
在整个应用程序中,只创建bean的一个实例

原型(Prototype):
每次注入都会创建一个新的bean。在没有获取bean对象时即通过Spring上下文获取的时候,,bean对象并不会被创建。

会话(Session):
在Web应用中,为每个会话创建一个bean实列

请求(Request):
在Web应用中,为每个请求创建一个bean实列。一次请求和当前请求的转发。

bean的生命周期

可以通过bean标签的属性init-method,destory-method来配置。

单例:

出生:容器创建,对象就出生。

活着:只要容器在,对象就一直存在。

死亡:容器销毁,对象消亡。

多例:

出生:每次获取bean对象时,创建对象。

活着:只要对象在使用中,就一直活着。

死亡:当对象长时间不使用,并且也没有别的对象引用时,由Java垃圾回收器回收。

spring的依赖注入

运行期提供变量的值就叫依赖注入。缺什么,以配置文件的方式告诉spring传什么。

注入方式有三种:
第一种:使用构造函数注入
第二种:使用set方法注入
第三种:使用注解注入

注入的数据类型有三种
第一种:基本类型和String类型
第二种:其它bean类型(必须实在Spring的配置文件中出现过的bean)
第三种:复杂类型(集合类型)

复杂类型注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<bean id="customerDao" class="cn.demo.dao.impl.ICustomerDaoImpl">
<property name="str">
<array>
<value>123</value>
<value>123</value>
</array>

</property>
<property name="list">
<list>
<value>abc</value>
<value>abc</value>
</list>
</property>
<property name="map">
<map>
<entry key="123" value="abc" />
<entry key="456">
<value>def</value>
</entry>
</map>
</property>
<property name="set">
<set>
<value>123</value>
<value>456</value>
</set>
</property>
<property name="pro">
<props>
<prop key="123" >456</prop>
</props>
</property>
</bean>

IOC-注解

用于创建bean对象

@Component:表示这个类需要在应用程序中被创建

属性:value。含义是指定bean的id。有默认值为当前类的短名首字母小写。
由这个注解衍生的三个注解:

@Controller 一般用于表现层的注解

@Service 一般用于业务层

@Repository 一般用于持久层
这三个通过继承@Component得到。

用于注入数据

@Autowired:自动满足bean之间的依赖
例子:

1
2
@Autowired
private CompactDisc compactDisc1;

@Autowried注入过程:首先拿对象类型到spring容器中查找是否有这个bean对象。如果存在多个类满足这个条件,就拿字段名与bean的id比较,一旦找到就注入进去,不再继续查找,否则报错。

多个类实现同一接口情形

在实现接口时,@Component写在实现类上。
@Autowired可以直接写在接口上也可以写在具体实现类上。

如果有多个类实现了一个接口,编译器就无法识别究竟要调用哪一个具体的实现类。

解决方法:

1、设置首选bean

这时可以通过设置首选bean(@Primary注解)以实现优先哪一个实现类注入。但这个方法也不是经常有效的,一旦有多个地方设置了@Primary,就会出现先前的错误。也就是只能设置一个@Primary。

2、使用限定符

在每个实现类上使用限定符,如@Qualifier(“festival”)。然后在test方法中指定限定符,注意festival是userService的实现类。

给类成员注入数据时,不能独立使用;在给方法的形参注入数据时,可以独立使用。

1
2
3
@Autowired
@Qualifier("festival")
private UserService userService

3、使用限定符和类id
可以这样写@Component(“festival”),指定每个实现类的id,然后在@Qualifier(“festival”)中指定要调用的id。实际上,如果不在@Component中指定id,那么这个实现类的默认id就是它的类名首字母小写。同样在@Qualifier(“userserviceFestival”)写上即可。

4、Java标准解决方案
使用@Resource,@Resource是java标准提供的方案,需要导入
import javax.annotation.Resource;

如:@Resource(name=”userServiceNormal”),其中name属性对应的值就是实现类的id

@ComponentScan:自动发现应用程序中创建的类

使用单元测试

1、引入Spring单元测试模块
maven:junit、spring-test
@RunWith(SpringJUnit4ClassRunner.class)

2、加载配置类
@ContextConfiguration(classes=AppConfig.class)

注解@Autowired使用的四种情形

1、有参构造器
2、成员变量
3、set方法
4、任意方法(带有相关参数)

定义配置类:

@Configuration:表示当前类是一个配置类
@Bean:它是把方法的返回值存入spring容器。该注解有一个属性,name:用于指定bean的id。当不指定时它有默认值为方法的名称。
@Import({demo.class}),导入其它配置类。如果不要用类对象里的方法就用Import导入,如果要用类对象方法就用@Component

1
2
3
4
@Bean(name="name")
public int getId(int id) {
return id;
}

设置组件扫描的基础包

AppConfig.java在不做任何其它配置的情况下只能扫描当前包下的类。
要扫描其它包下的类可以通过一些配置来实现

1、设置单个包@ComponentScan(“com.demo”),扫描demo包下所有类。

2、设置多个包@ComponentScan(basePackages = {“com.demo1”,”com.demo2”})。

3、设置多个实现类@ComponentScan(basePackageClasses = {UserServiceFestival.class,UserService.class})。

4、配置xml文件启用组件扫描

相关标签:
component-scan

IDEA在resources目录下创建applicationContext.xml文件。

告知spring在创建容器时要扫描的包。当配置了此标签之后,spring创建容器就回去指定的包及其子包下找相应的注解。

注意:标签是在一个context的名称空间里,所以必须先导入context名称空间。
在bean.xml文件中写入:

1
<context:component-scan base-package="com"/>

这样就是扫描com包下的类。

在测试类中写入

1
@ContextConfiguration("classpath:applicationContext.xml")

即可。

AOP

动态代理

作用:不改变源码的基础上,对已有方法增强。(它是AOP思想的实现技术)

分类:基于接口的动态代理、基于子类的动态代理

1、基于接口的动态代理,要求被代理类至少实现一个接口
创建代理对象的方法:newProxyInstance(ClassLoader,Class[],InvocationHandler)。
ClassLoader:类加载器。和被代理对象使用相同的类加载器。一般都是固定写法。
Class[]:字节码数组.被代理类实现的接口。(要求代理对象和被代理对象具有相同的行为)。
InvocationHandler:它是一个接口,就是用于我们提供增强代码的。一般都是写一个该接口的实现类。实现类可以是匿名内部类。它的含义就是:如何代理。此处的代码只能是谁用谁提供。

示例1:

IActor.java

1
2
3
4
5
6
package com.aop.demo;

public interface IActor {
void basicAct(float money);
void dangerAct(float money);
}

com.aop.demo.IActor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.aop.demo.impl;

import com.aop.demo.IActor;

public class Actor implements IActor {
@Override
public void dangerAct(float money) {
System.out.println("危险的表演" + money);
}

@Override
public void basicAct(float money) {
System.out.println("基础的表演" + money);
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.aop.demo;

import com.aop.demo.impl.Actor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
public static void main(String[] args) {
IActor ac = new Actor();
// ac.basicAct(1000f);
// ac.dangerAct(4000f);

IActor proxyActor = (IActor) Proxy.newProxyInstance(ac.getClass().getClassLoader(), ac.getClass().getInterfaces(), new InvocationHandler() {
/**
* 执行被代理对象的任何方法都会经过该方法,该方法有拦截的功能
*
* proxy :代理对象的引用。不一定每次都会有
* method:当前执行的方法
* args:当前执行方法所需的参数
* 返回值:当前执行方法的返回值
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
float money = (float) args[0];
Object all = null;
if("basicAct".equals(method.getName())) {
if(money > 10000) {
all = method.invoke(ac,money);
}
}
if("dangerAct".equals(method.getName())) {
if(money > 40000) {
all = method.invoke(ac,money);
}
}
return all;
}
});
proxyActor.basicAct(1000f);
}
}

示例2(数据库连接池原理):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static Connection getConnetion() {
final Connection conn = pool.remove(0);
Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object rtValue = null;
if("close".equals(method.getName())) {
//不能直接关闭连接
pool.add(conn);
}
else {
rtValue = method.invoke(conn,args);
}
return rtValue;
}
});
return proxyConn;
}

概念

aop,面向切面编程。
简单来说就是把程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对已有的方法进行增强。

基于xml的aop配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="customerService" class="com.aop2.demo.impl.CustomerServiceImpl"></bean>
<!--基于xml的aop配置-->
<!--1、把通知类交给spring来管理-->
<bean id="logger" class="com.aop2.demo.utils.Logger"></bean>
<!--2、导入aop名称空间,并且使用aop:config开始aop的配置-->
<aop:config>
<!--3、使用aop:aspect配置切面,id属性用于给切面提供一个唯一标识。ref属性:用于应用通知bean的id-->
<aop:aspect id="logAdvice" ref="logger">
<!--4、配置通知的类型,指定增强的方法何时执行。
method:用于指定增强方法名称
pointcut属性:用于指定切入点表达式
-->
<aop:before method="printLog" pointcut="execution(public void com.aop2.demo.impl.CustomerServiceImpl.saveCustomer())"></aop:before>
</aop:aspect>
</aop:config>
</beans>

环绕通知:是spring框架为我们提供的一种可以在代码中手动控制通知方法什么时候执行的方法。

ProceedingJoinPoint:该接口可以作为环绕通知的方法参数来使用。在程序运行时,spring框架会为我们提供接口的实现类,供我们使用。该接口有一个方法:proceed(),它的作用就等同于method.invoke()方法,就是明确调用业务层核心方法(切入方法)。

-------------本文结束感谢您的阅读-------------
0%