有异常抛到controller层
spring主要运用反射,所以使用spring要时刻保持空构造器的存在
创建对象
bean 标签
id:自定义的唯一名;
class:必须是具体的实现类名;
scope:表示改对象的状态:singleton|prototype.默认是单例,prototype设置为多例.很少使用,struts2
lazy-init=“true”:对象仍然是单例,变为懒汉单例.
init-method=“” :指定初始化方法,当bean被实例化后,立即执行初始化方法.
destroy-method=“”: 指定销毁方法,当容器销毁后,容器中bean自动执行销毁方法释放资源.包扫描:检测注解批量创造对象
<context:component-scan base-package="包名"></context:component-scan>
bean的id不可重复
<bean id="Run" class="test01.RunImpl" init-method="init" destroy-method="destroyed"></bean>
包扫描注意:
识别的注解如下:
@Controller用于标注控制层组件;
@Service用于标注业务层组件;
@Repository用于标注数据访问组件,即DAO组件;
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注;
context:component-scan不仅仅识别这四个注解
注意点2:通过注解方式注册的bean,默认id是类名首字母小写;可以自定义id
注意点3:这四个注解当前是没有区别,效果都是注册bean到spring容器,后续springMVC框架有区别,我们需要有好的代码习惯,分别注解各层的bean对象。
注意点4:不能注解到接口类上。
<context:component-scan base-package="controller"></context:component-scan>
创建复杂对象
以获取sqlsessionFactory实例为例
思路:–>
sqlsessionFaxtory对象的创建是有条件的,首先要先获取mabayis.xml文件,
通过文件输入流创建sqlsessionfactory对象,这两件事情被封装进了a()方法中,所以还需要调用a方法才能返回对象
体现在xml中,要先传参(mabayis.xml文件)或者说set注入,再利用传完参之后的对象调用a()方法
<bean id="complex" class="test02.Complex">
<property name="p" value="mabatis.xml"></property>
</bean>
<!-- factory-bean 引用对象 factory-method 调用方法 类似于对象调方法-->
<bean id="getObj" factory-bean="complex" factory-method="a"></bean>
类
public class Complex {
String p;
public SqlSessionFactory a() throws IOException {
InputStream rs = Resources.getResourceAsStream(p);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(rs);
return build;
}
public void setP(String p) {
this.p = p;
}
}
测试
@Test
public void getComplexObj() throws IOException {
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("complex.xml");
SqlSessionFactory getObj = (SqlSessionFactory) ca.getBean("getObj");
System.out.println(getObj);
}
@Test
public void getComplexObj2() throws IOException {
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("complex.xml");
Complex bean = ca.getBean(Complex.class);
System.out.println(bean.a());
}
获得对象
获得对象的方式有两种,通过标签id名(需强转),通过类对象。默认是单例的。所以无论用什么方式获得的对象都是同一个对象,目的是为了节省资源
@Test
public void t1(){
//获得容器
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("spring.xml");
//获得容器中的的对象
RunImpl runImpl = (RunImpl)ca.getBean("Run");
RunImpl bean = ca.getBean(RunImpl.class);
bean.run();
runImpl.run();
System.out.println(runImpl==bean);
ca.close();
}
创建对象并给对象中的属性赋值
<property>
value:简单类型赋值;ref:复杂对象赋值,引用其他bean对象id
1.set注入(类中必须有set方法)
<bean id="p" class="test04.PropertiesTest">
<property name="i" value="54"></property>
<property name="r" ref="Run"></property>
<property name="s" value="lili"></property>
</bean>
2.构造器注入
<bean class="test04.PropertiesTest2">
<constructor-arg index="0" ref="Run"></constructor-arg>
<constructor-arg name="s" value="zhangsan"></constructor-arg>
<constructor-arg name="l">
<list>
<value>3</value>
<value>5</value>
<value>7</value>
</list>
</constructor-arg>
<constructor-arg index="3">
<map>
<entry key="lili" value="18"></entry>
<entry key="jack" value="21"></entry>
</map>
</constructor-arg>
</bean>
public class PropertiesTest2 {
private RunImpl r; //复杂属性
private String s; //简单属性
private List<Integer> l; //简单或复杂根据泛型决定,这里是一个简单属性
private Map<String,Integer> m;
public PropertiesTest2() {
}
public PropertiesTest2(RunImpl r, String s, List<Integer> l, Map<String, Integer> m) {
this.r = r;
this.s = s;
this.l = l;
this.m = m;
}
3.关于复杂对象,也可以内部bean注入(类似内部类),在只使用一次的场景
<constructor-arg index="0">
<bean class="com.javasm.sys.service.impl.UserServiceImpl2"></bean>
</constructor-arg>
4.注解注入,和包扫描结合使用
@Autowired,@Resource
在包扫描(加了注解)的类中,在属性或构造器加上以上注解,会自动==在复杂对象(实体类属性)==中实现注入,简单属性(基本数据类型+String )放注解会报错(自动装配只能装配已有的东西,只有通过包扫描生成的对象是已有的东西,所以只能把对象注入属性,包扫描的对象默认id是类名首字母小写,有了id,就可以把该对象赋给其他属性)
@Controller
public class ScanController2 {
@Autowired
private ScanController s;
private String m;
public void scan(){
System.out.println("我被扫描了");
}
public ScanController getS() {
return s;
}
public void setS(ScanController s) {
this.s = s;
}
public String getM() {
return m;
}
public void setM(String m) {
this.m = m;
}
}
包扫描,扫描所有注解并进行生成对象或对属性注入,通过name或类型对属性注入。
@Autowired先byType,再byName
@Resource先byName,再byType
<context:component-scan base-package="controller"></context:component-scan>
其他spring标签
通过
<context:property-placeholder>
标签,可以加载本地文件,获得本地文件中的数据(获得数据源的第一步是先有这些xml配置),而xml文件可以通过¥{}来获取值,以Druid数据库连接池为例
属性:location 后跟本地配置文件
<!-- 读取配置文件,把读到的值通过${}方式注入到容器中的属性中-->
<context:property-placeholder location="a.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
测试类
@Test
public void getDataSource() throws SQLException {
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("ScanTest.xml");
DataSource dataSource = (DataSource)ca.getBean("dataSource");
System.out.println(dataSource);
DataSource bean = ca.getBean(DataSource.class);
System.out.println(bean);
}
导入其他xml
<import resource="dao.xml"></import>
自定义配置数据
与在xml文件中给属性注入不同,还有一种方式是利用注解在set方法上注入
在这个过程中报了一个错误,自定义的复杂属性(实体)无法被解析,因为没有引入实体类所在的xml文件,引入之后就可以自动注入了ignore-unresolvable:忽略不可解析的key值.
<!-- 读取配置文件中的信息-->
<context:property-placeholder location="my.properties" ignore-unresolvable="true"></context:property-placeholder>
<!-- 包扫描test05包下带注解的实体类和属性,创建对象并给复杂属性注入-->
<context:component-scan base-package="test05"></context:component-scan>
<!-- 为了获得runImpl对象引入另一个xml文件-->
<import resource="spring.xml"></import>
实体类
@Component
public class User{
private String name;
private Integer age;
@Autowired
private RunImpl ri; //拥有跑步方法
public String getName() {
return name;
}
@Value("${my.name}")
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
@Value("${my.age}")
public void setAge(Integer age) {
this.age = age;
}
public RunImpl getRi() {
return ri;
}
public void setRi(RunImpl ri) {
this.ri = ri;
}
}
测试类
@Test
public void t2(){
//获得容器
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("custom.xml");
User bean = ca.getBean(User.class);
System.out.println(bean); //User{name='pb', age=18, ri=test01.RunImpl@1fb669c3}
}
其他注解
@Scope用于指定scope作用域的(用在类或方法上),等价于bean标签的scope属性
@PostConstruct用于指定初始化方法(用在方法上),等价于bean标签的init-method属性
@PreDestory用于指定销毁方法(用在方法上),等价于bean标签的destory-method属性
Aop
当我们想要拓展或修改某个类中的功能(方法)时,我们可以选择spring中的aop注解或xml实现。我们想要增加或者修改的功能叫做切面对象,针对切面对象,我们可以借助注解在不同的时机对其做出改变
基于代理模式实现的对目标对象方法(非核心业务)进行修改。所以也称为面向切面编程
使用aop前需要引入的依赖
<!--spring集成aspectj包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
<!--第三方的aop组件包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.8</version>
</dependency>
<!--cglib动态代理包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
注解使用Aop
注意:aop是基于动态代理模式实现的,所以在使用的时候获得的bean对象应该是接口,才能正常使用代理对象调用使用aop后的方法
步骤:
1.在xml中开启aop注解识别;
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.编写切面类,通知方法;
定义切面bean对象:@Component+@Aspect
定义通知方法:@Before+@AfterReturning+@AfterThrowing+@After
3.定义切入点表达式
@Pointcut(“execution(返回值 包名.类.方法(…))”)
4.可以定义环绕通知取代以上四类通知:
环绕通知方法:方法必须有返回值Object;方法形参必须是ProceedingJoinpoint
@Around(“userserviePc()”)
public Object aroundAdvice(ProceedingJoinPoint jp) {
Object result = null;
try {
System.out.println(“前置” + jp.getTarget().getClass().getName());
result = jp.proceed();//Object result = method.invoke(target,args);
System.out.println(“返回:” + result);
} catch (Throwable throwable) {
System.out.println(“异常:” + throwable);
} finally {
System.out.println(“最终”);
}
return result;
}
//接口
public interface IPay {
boolean pay(double money);
}
//目标对象
@Component
public class AliPay implements IPay{
@Override
public boolean pay(double money) {
Log.logPrint("支付成功");
if(money>0){
System.out.println("支付宝支付"+money+"元");
return true;
}
return false;
}
public void test(){
System.out.println("这里是业务逻辑");
}
}
//切面对象
@Component //自动装配bean对象
@Aspect //自动识别是一个切面bean
public class Log2 {
//对目标对象定位,用一个方法承载目标对象位置
@Pointcut("execution(* day0901.IPaytest.AliPay.pay(..))") //所有叫pay()的方法都是连接点对象
public void AliPay(){}
@Before("AliPay()")
public void beforeAdvice(JoinPoint pj){
pj.getTarget();//目标对象
Object[] args = pj.getArgs();//实参列表
MethodSignature signature = (MethodSignature)pj.getSignature(); //方法签名
Method method = signature.getMethod();//连接点方法
System.out.println(method.getName());
System.out.println("我是前置通知");
}
@After("AliPay()")
public void afterAdvice(){
System.out.println("我是最终通知");
}
joinpoint参数可以让切面对象获得目标对象的信息,可以不加
环绕
所谓切入点其实就是目标对象的指定方法,当使用环绕通知,环绕通知特有的形参ProceedingJoinPoint,有一个方法proceed,可以获得切入点的返回值,并抛出一个异常,围绕proceed方法的前/后/异常/finally都可以进行操作。使用环绕通知注解的方法一定要有object类型的返回值,如果这个方法无返回值,那么作为调用改代理方法的类无法获得真正的结果。
@Component
@Aspect //切面bean
public class Log2 {
//对目标对象定位,用一个方法承载目标对象位置
@Pointcut("execution(* day0901.IPaytest.*.pay(..))") //所有叫pay()的方法都是连接点对象
public void AliPay(){}
@Around("AliPay()")
public Object aroundAdvice(ProceedingJoinPoint pj){
Object proceed=null;
//执行proceed方法可以得到切入点的返回值
try {
Mylog annotation = pj.getTarget().getClass().getAnnotation(Mylog.class);
if(annotation!=null){
System.out.println(annotation.aop()); //动态的获得注解中的值
}
System.out.println("前"+pj.getTarget().getClass().getName());
proceed = pj.proceed();
System.out.println("返回"+proceed);
} catch (Throwable throwable) {
System.out.println("异常"+throwable);
throwable.printStackTrace();
}finally {
System.out.println("最终");
}
return proceed;
}
自定义注解
将自定义注解Mylog的位置放到切入点注解,@Mylog成为切入点注解标志,想让哪个方法成为切入点,只需在该方法上标注@Mylog。这种方式相对于直接填写方法名更加灵活
@Pointcut("@annotation(com.javasm.sys.aspects.Mylog)")
元注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE}) //目的是为了标记切入点,所以仅在方法上生效
public @interface Mylog {
String value() default "";
String aop() default "";
}
加了注解后
@Component
public class AliPay implements IPay{
@Mylog(aop="开始支付")
@Override
public boolean pay(double money) {
Log.logPrint("支付成功");
if(money>0){
System.out.println("支付宝支付"+money+"元");
return true;
}
return false;
}
public void test(){
System.out.println("这里是业务逻辑");
}
}
aop实现事务
打开事务 异常回滚 提交事务 关闭数据源
弄不完了,明天再弄