spring

有异常抛到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实现事务
打开事务 异常回滚 提交事务 关闭数据源
弄不完了,明天再弄

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值