Blog

java安装配置-安装版本选择

java 下载时,经常 会看到 有两个 版本:
比如
http://www.oracle.com/technetwork/cn/java/javase/downloads/jdk8-downloads-2133151-zhs.html

Java SE Development Kit 8u171
Java SE Development Kit 8u172

那我们究竟应该 下载 哪个版本 比较好呢。

简单来说,奇数版本 是修复了之前 的严重错误  的版本。经过了很多测试。
下一代奇数版本,将包括上一代偶数版本所修复的全部bug。

偶数版本,修复了之前严重错误 和 额外错误的版本。仍需要机构再多测试。

查看 网站讨论 链接:https://www.reddit.com/r/java/comments/8d3i9e/jdk_8u171_vs_jdk_8u172/?st=jhk8n1q9&sh=49328aa6

官方说明链接:
http://www.oracle.com/technetwork/java/javase/cpu-psu-explained-2331472.html

Java CPU and PSU Releases Explained

October 14, 2014

Starting with the release of Java SE 7 Update 71 (Java SE 7u71) in October 2014, Oracle will release a Critical Patch Update (CPU) at the same time as a corresponding Patch Set Update (PSU) for Java SE 7.


Which Java version should I choose: the CPU or the PSU?

Oracle strongly recommends that all Java SE users upgrade to the latest CPU release available for a release family. Most users should choose the CPU release.

Users should only use the corresponding PSU release if they are being impacted by one of the additional bugs fixed in that version as noted in the release notes.

The subsequent CPU release will contain all of the fixes from the current PSU. For this reason, organizations should test the current PSU in their environment in anticipation of these fixes being included in the next CPU.

What is the difference between a Java CPU and PSU release?

Java SE Critical Patch Updates (CPU) contain fixes to security vulnerabilities and critical bug fixes. Oracle strongly recommends that all Java SE users upgrade to the latest CPU releases as they are made available. Java SE CPU releases are odd numbered versions (i.e. 7u71, 7u65 – see more on Java SE version numbering schemes here).

Java SE Patch Set Updates (PSU) contain all of fixes in the corresponding CPU, as well as additional non-critical fixes. Java PSU releases should only be used if you are being impacted by one of the additional bugs fixed in that version. The release notes call out the additional fixes available in Java SE PSU releases.

Is the cadence of CPU releases changing?

As before, Java SE CPU releases are scheduled for release on the Tuesday closest to the 17th day of January, April, July and October under the normal Oracle Critical Patch Update schedule.

Starting in October 2014 with the release of Java SE 7u71 (CPU) and Java SE 7u72 (PSU), Oracle plans to additionally release a corresponding PSU release along with each CPU release for Java SE 7. PSU releases provide organizations and developers with access to non-critical fixes in addition to the critical fixes contained in the corresponding CPU.

Resources and Additional Information:

 


发布说明:release note
http://www.oracle.com/technetwork/java/javase/8u-relnotes-2225394.html

This page lists all the release notes for public releases and Bundled Patch Release (BPR) builds of JDK 8.

BPR builds are available only as commercial offerings to Oracle customers.

For a complete listing of release notes for all JDK 8 update releases in a single page, see Release Notes for JDK 8 and JDK 8 Update Releases. This page compiles information for many releases (loading this page through slow connections will take longer than loading release notes for individual releases).

 

eclipse-注释模板自动填充注释

开发时,经常看到 别人的 代码注释是 很规范。
一开始 我觉得 人家很 认真啊,每一个 方法 的注释 都是 自己一行一行 敲出来的 啊 。
后来,我觉悟到,注释 可以 自动 生成的 啊 。。。

设置注释模板的入口:Window->Preference->Java->Code Style->Code Template 然后展开Comments节点就是所有需设置注释的元素。本文现就每一个元素逐一给大家介绍一下。
文件(Files)注释标签:

/**
* @Title: ${file_name}
* @Package ${package_name}
* @Description: ${todo}(用一句话描述该文件做什么)
* @author ${user}
* @date ${date}
* @version V1.0
*/

类型(Types)注释标签(类的注释):

/**
* @ClassName: ${type_name}
* @Description: ${todo}(这里用一句话描述这个类的作用)
* @author ${user}
* @date ${date}
*
* ${tags}
*/

字段(Fields)注释标签:

/**
* @Fields field:field:{todo}(用一句话描述这个变量表示什么)
*/

构造函数(Constructor)标签:

/**
* 创建一个新的实例 ${enclosing_type}.
*
* ${tags}
*/

方法(Methods)标签:

/**
* @Title: ${enclosing_method}
* @Description: ${todo}(这里用一句话描述这个方法的作用)
* @param ${tags}    参数
* @return ${return_type}    返回类型
* @throws
*/

覆盖方法(Overriding Methods)标签:

/* (非 Javadoc)
* <p>Title: ${enclosing_method}</p>
* <p>Description: </p>
* ${tags}
* ${see_to_overridden}
*/

代理方法(Delegate Methods)标签:

/**
* ${tags}
* ${see_to_target}
*/

getter方法标签:

/**
* @return ${bare_field_name}
*/

setter方法标签:

/**
* @param paramtheparamthe{bare_field_name} to set
*/

要实现上面的注释模板,这需要将下面的配置文件导入就可以了:

<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template
                autoinsert="false"
                context="filecomment_context"
                deleted="false"
                description="Comment for created Java files"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.filecomment"
                name="filecomment">
    /**  
    * @Title: ${file_name}
    * @Package ${package_name}
    * @Description: ${todo}(用一句话描述该文件做什么)
    * @author ${user}
    * @date ${date}
    * @version V1.0  
    */
    </template>
    <template
                autoinsert="false"
                context="typecomment_context"
                deleted="false"
                description="Comment for created types"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.typecomment"
                name="typecomment">
    /**
    * @ClassName: ${type_name}
    * @Description: ${todo}(这里用一句话描述这个类的作用)
    * @author ${user}
    * @date ${date}
    *
    * ${tags}
    */
    </template>
    <template
                autoinsert="false"
                context="fieldcomment_context"
                deleted="false"
                description="Comment for fields"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.fieldcomment"
                name="fieldcomment">
    /**
    * @Fields field:field:{todo}(用一句话描述这个变量表示什么)
    */
    </template>
    <template
                autoinsert="false"
                  context="constructorcomment_context"
                deleted="false"
                description="Comment for created constructors"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.constructorcomment"
                name="constructorcomment">
    /**
     * 创建一个新的实例 ${enclosing_type}.
     *
     * ${tags}
     */
    </template>
        <template
                autoinsert="false"
                context="methodcomment_context"
                deleted="false"
                description="Comment for non-overriding methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.methodcomment"
                name="methodcomment">
    /**
    * @Title: ${enclosing_method}
    * @Description: ${todo}(这里用一句话描述这个方法的作用)
    * @param ${tags}    参数
    * @return ${return_type}    返回类型
    * @throws
    */
    </template>
    <template
                autoinsert="true"
                context="overridecomment_context"
                deleted="false"
                description="Comment for overriding methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.overridecomment"
                name="overridecomment">
    /* (非 Javadoc)
    * <p>Title: ${enclosing_method}</p>
    * <p>Description: </p>
    * ${tags}
    * ${see_to_overridden}
    */
    </template>
        <template
                autoinsert="true"
                context="delegatecomment_context"
                deleted="false"
                description="Comment for delegate methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.delegatecomment"
                name="delegatecomment">
    /**
     * ${tags}
     * ${see_to_target}
     */
    </template>
    <template
                autoinsert="false"
                context="gettercomment_context"
                deleted="false"
                description="Comment for getter method"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.gettercomment"
                name="gettercomment">
    /**
    * @return ${bare_field_name}
    */
    </template>
    <template
                autoinsert="true"
                context="settercomment_context"
                deleted="false"
                description="Comment for setter method"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.settercomment"
                name="settercomment">
    /**
     * @param paramtheparamthe{bare_field_name} to set
     */
    </template>
</templates>

参考:https://blog.csdn.net/paul342/article/details/52982585

在实际开发过程中,新建一个类以后,通过输入/**,然后回车即自动按照刚才定义的注释模板格式补齐注释内容,也可以按shift+alt+j快捷键完成该动作

spring特点-DI依赖注入实现原理

以前一直有个疑惑,为什么我创建的controller中注入的service类有时候是代理类,有时候是普通javabean,当时能力不够,现在已经有了点经验就大胆跟了跟源码,看看到底咋回事。

首先看看问题现象:

a1:service是代理类,并且是CGLIB类型代理  

 

a2:service是代理类,并且是jdk 动态代理

b:serivce不是代理类,而是普通类

问题分析

我对service类进行了以下的测试:(前提开启事务注解<tx:annotation-driven/>)

1)service方法添加@Transactional注解或者加入其它的aop拦截配置,没有实现任何接口。   对应问题现状 a1

2)service方法添加@Transactional注解或者加入其它的aop拦截配置,实现了接口。              对应问题现状a2

3)serice方法没有添加@Transactional注解或者其它的aop拦截配置。                                      对应问题现状b

看来出现这种问题的原因就是spring的问题,因为这个类是它创建的,这就需要我们来看下spring创建bean的代码,由于spring太庞大了

我们只看最关键的部分,在创建bean是都会调用getBean()方法,

@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
       @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    return createBean(beanName, mbd, args);
}

经过不断的流转会进入AbstractAutowireCapableBeanFactory的createBean方法

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
     try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
               logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
      }
      catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
          // A previously detected exception with proper bean creation context already,
          // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
          throw ex;
      }

 }

然后调用doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
if (instanceWrapper == null) {
       
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }return exposedObject;
    }

然后进入核心的createBeanInstance方法,省去了不相关方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
}

然后调用instantiateBean进行bea的实例化

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

实例化时会调用SimpleInstantiationStrategy的instantiate方法

@Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

通过上面的代码分析,如果Bean定义中没有方法覆盖,就使用JDK的反射机制进行实例化,否则,使用CGLIB进行实例化。

1、通过CGLIB的方式instantiateWithMethodInjection(bd, beanName, owner)
2、或者java的反射方式BeanUtils.instantiateClass(constructorToUse)
实例化一个bean

这是时候都是一个纯洁无瑕的javabean,那每个bean的额外加工,例如为某个bean添加事务支持,添加aop配置,还有就是将springmvc的controller进行url和handler的映射,等等这些都是在spring的扩展点完成的,回到
上面的doCreateBean方法,执行完实例化bean后执行
1、populateBean(beanName, mbd, instanceWrapper);
其中的populateBean是为了给生成的bean装配属性,
2、initializeBean(beanName, exposedObject, mbd);
这不是我们这次讨论的重点,关键是initializebean方法 ,为了配置bean的拓展方法 aop 事务等

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

这个方法就是对生成的bean进行一些扩展处理,主要就是这个方法会调用我们自定义的扩展点
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到这里是获取所有的beanProcessor,调用postProcessAfterInitialization方法,我们要关注是的一个叫InfrastructureAdvisorAutoProxyCreator的扩展类。

/**
 * Auto-proxy creator that considers infrastructure Advisor beans only,
 * ignoring any application-defined Advisors.
 *
 * @author Juergen Hoeller
 * @since 2.0.7
 */
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {}

看下这个类的注释可以发现这个类是为配置了aop配置(包括注解和xml配置两种方式)的类,生成代理类。
核心方法是下面这个方法wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

下面解析下这个函数

首先看下getAdvicesAndAdvisorsForBean这个方法:名字很明显用来获取当前bean的advisor和adices的,这些都是生成代理类时需要的信息。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

然后调用findEligibleAdvisors,获取配置的advisor信息

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

来看下findCandidateAdvisors方法,最终调用BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<>();
        }

        List<Advisor> advisors = new LinkedList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                      throw ex;
                    }
                }
            }
        }
        return advisors;
    }

1)首先获取spring管理的Advisor类型的类名称。

2)通过beanFactory获取该bean对应的实体类,并装入advisors。

生成的这个advisor可是相当复杂,这里我们以事务advisor为例说明

可以看到这个advisor包含了advice(aop中的通知),pointcut(aop中的切入点),


advice是TransactionInterceptor,这个通知是用来管理spring的事务的可以看到包含事务的管理器等管理事务的属性,具体的方法见TransactionAspectSupport.invokeWithinTransaction
pointcut是TransactionAttributeSourcePointcut,

public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

这个是pointcut的核心方法,用来匹配某个类是否符合事务管理的aop拦截要求。
ok,回到之前的wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

我们之前分析道getAdvicesAndAdvisorsForBean方法,可以看到如果得到的结果是DO_NOT_PROXY,就会将这个bean直接返回,

如果不是DO_NOT_PROXY,(其实DO_NOT_PROXY就是null,但是使用DO_NOT_PROXY会使得代码逻辑更加清晰),就会执行

createProxy方法,创建一个代理类,然后返回一个代理类,ok,现在我们就清楚了问题分析中的 第3)和第 1) 2) 区别,那就是

service类是否配置了相关的aop拦截配置,无论是注解还是xml形式,目前我们还不清楚第1)和 第2)的区别,就是为什么有时候

生成jdk代理,有时候生成cglib代理,这就需要继续向下看creatProxy方法了,最终会进入一个DefaultAopProxyFactory的createAopProxy

方法:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

如果目标类是接口就一定会使用jdk代理,如果目标类没有可以代理的接口就一定会使用Cglib代理。

问题总结

这个问题我们现在知道了,那他有什么意义呢,换句话说,我们为什么要知道这个,即使不知道原理,我们也可以去搜搜去解决,在我看来
把他弄明白的过程学会了很多知识,而且我们如果在工作过程中遇到了需要扩展的地方,我们可以很容易的去解决。

最后欢迎大家在评论区留言,有什么想法说出来,共同进步。

来自:https://www.cnblogs.com/zcmzex/p/8822509.html

 

 

参考另一篇IOC源码理解分析:

总结:

//AbstractAutowireCapableBeanFactory创建Bean实例对象:
//AbstractAutowireCapableBeanFactory类实现了ObejctFactory接口,创建容器指定的Bean实例对象,同时还对创建的Bean实例对象进行初始化处理。
//  创建Bean实例对象  方法中 调用了 核心 三句话  
   protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)  
  
//调用一 :createBeanInstance:生成Bean所包含的java对象实例。
          instanceWrapper = createBeanInstance(beanName, mbd, args);  
//调用二:populateBean :对Bean属性的依赖注入进行处理(解析bean的属性,并对bean的属性赋值[值注入])
          populateBean(beanName, mbd, instanceWrapper);  
//调用三:对 bean 的 扩展功能 进行配置 比如 aop ,事务。(可以 插入到任何操作 后)    
// BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件  
        exposedObject = initializeBean(beanName, exposedObject, mbd);  

// 创建bean的 过程 都是 普通的 实例bean ,如果 是接口 就执行 不创建。
// 然后  bean 赋值 过程 没有变化,
// 到了 bean 拓展功能阶段,就看 有没有 代理 或者 接口问题了。比如 上面的 举例  明天写后续

spring特点-DI依赖注入三种方式

一、Spring 容器 自动创建的Bean的方法有两种:

1、在xml文件配置 (即spring 配置文件)
在xml文件中 配置bean, 标注 各个bean之间的依赖关系。
2、(1)在代码中用注解:
在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。

constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。

byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

byType:查找所有的set方法,将符合符合参数类型的bean注入。

下面进入正题:注解方式注册bean,注入依赖

主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
@Component:可以用于注册所有bean
@Repository:主要用于注册dao层的bean
@Controller:主要用于注册控制层的bean
@Service:主要用于注册服务层的bean

描述依赖关系主要有两种:
@Resource:java的注解,默认以byName的方式去匹配与属性名相同的bean的id,如果没有找到就会以byType的方式查找,如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;

public UserService(){

}

@Autowired:spring注解,默认也是以byName的方式去匹配与属性名相同的bean的id,如果没有找到,就通过byType的方式去查找,如果查找到多个,用@Qualifier注解限定具体使用哪个。

@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;<br>

(2)同时在spring 配置文件中 配置自动扫描的包:

 <!-- 配置自动扫描的包 -->
 <context:component-scan base-package="com.webproject.srcdir">             
 </context:component-scan>   <br>
在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean
注意:如果配置了<context:component-scan>那么<context:annotation-config/>标签就可以不用再xml中配置了,因为前者包含了后者。另外<context:component-scan>还提供了两个子标签

1.     <context:include-filter>
2.     <context:exclude-filter>

在说明这两个子标签前,先说一下<context:component-scan>有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。所以如果仅仅是在配置文件中这么写
<context:component-scan base-package="com.webproject.srcdir"/>

 Use-default-filter此时为true那么会对base-package包或者子包下的所有的进行java类进行扫描,并把匹配的java类注册成bean。


 可以发现这种扫描的粒度有点太大,如果你只想扫描指定包下面的Controller,该怎么办?此时子标签<context:incluce-filter>就起到了勇武之地。如下所示

<context:component-scan base-package="com.webproject.srcdir.controller">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

但是因为use-dafault-filter在上面并没有指定,默认就为true,所以当把上面的配置改成如下所示的时候,就会产生与你期望相悖的结果(注意base-package包值得变化)

<context:component-scan base-package="com.webproject.srcdir">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

此时,spring不仅扫描了@Controller,还扫描了指定包所在的子包service包下注解@Service的java类

此时指定的include-filter没有起到作用,只要把use-default-filter设置成false就可以了。这样就可以避免在base-packeage配置多个包名这种不是很优雅的方法来解决这个问题了。

另外在我参与的项目中可以发现在base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定<context:exclude-filter>来进行过滤,说明此包不需要被扫描。综合以上说明

Use-dafault-filters=”false”的情况下:<context:exclude-filter>指定的不扫描,<context:include-filter>指定的扫描

写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码嘛。

二、Spring 依赖注入 三种方式:

(Dependency Injection) 依赖注入:两个对象之间的依赖关系在程序运行时由外部容器动态的注入依赖行为方式称为依赖注入(DI) 。 DI 是 IOC 的一种形式。

IoC 在应用开发中是一个非常有力的概念。如 Martin Flower 所述, IoC的一种表现形式就是依赖性注射。依赖性注射用的是好莱坞原则, ” 不要找我,我会找你的。 “。换句来说,你的类不会去查找或是实例化它们所依赖的类。控制恰好是反过来的,某种容器会设置这种依赖关系。使用 IoC常常使代码更加简洁,并且为相互依赖的类提供一种很好的方法。

依赖注入的三种实现类型:接口注入、 Setter 注入和构造器注入。
1、接口注入:

public class ClassA {
private InterfaceB clzB;
public void doSomething() {
Ojbect obj=Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
clzB.doIt()
}
……
}

上面的代码中, ClassA 依赖于 InterfaceB 的实现,如何获得 InterfaceB实现类的实例?传统的方法是在代码中创建 InterfaceB 实现类的实例,并将起赋予 clzB 。
而这样一来, ClassA 在编译期即依赖于 InterfaceB的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码.
我们根据预先在配置文件中设定的实现类的类名 (Config.BImplementation) ,动态加载实现类,并通过InterfaceB 强制转型后为 ClassA 所用。这就是接口注入的一个最原始的雏形。
而对于一个 Type1 型 IOC 容器而言,加载接口实现并创建其实例的工作由容器完成,见如下代码。

public class ClassA {
private InterfaceB clzB;
public Object doSomething(InterfaceB b) {
clzB = b;
return clzB.doIt();
}
……
}

在运行期, InterfaceB 实例将由容器提供。
Type1 型 IOC 发展较早(有意或无意),在实际中得到了普遍应用,即使在 IOC的概念尚未确立时,这样的方法也已经频繁出现在我们的代码中。
下面的代码大家应该非常熟悉:

public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponseresponse)throws ServletException, IOException { …… } }

这也是一个 接口注入, HttpServletRequest 和 HttpServletResponse 实例由Servlet Container 在运行期动态注入。
参考 J2EE API文档: https://docs.oracle.com/javaee/6/api/
HttpServletRequest HttpServletRequest 都是个接口。
这两个接口是由各个 j2ee厂商提供的实现的。比如tomcat ,weblogic都有自己的j2ee实现。
可以说j2ee只是一堆规范和定义好的接口。实现是由J2EE厂商实现,实例化是由web容器实例化

java通过 动态代理 反射实列化接口,以此实现 接口注入。

2、set注入:
采用属性的set方法进行初始化,就成为set注入。
1)给普通字符类型赋值。

public class User{
   privateString username;
 
   publicString getUsername() {
       returnusername;
   }
   publicvoid setUsername(String username) {
      this.username= username;
   }
}

我们只需要提供属性的set方法,然后去属性文件中去配置好让框架能够找到applicationContext.xml文件的beans标签。标签beans中添加bean标签, 指定id,class值,id值不做要求,class值为对象所在的完整路径。bean标签再添加property 标签,要求,name值与User类中对应的属性名称一致。value值就是我们要给User类中的username属性赋的值。

<bean id="userAction"class="com.test.spring.action.User" >
<property name="username" value="admin"></property>
</bean>

2)给对象赋值

同样提供对象的set方法

public class User{
     private UserService userservice;
     public UserServicegetUserservice() {
          returnuser;
     }
     public void setUserservice(UserService userservice){
         this.userservice= userservice;
     }
}

配置文件中要增加UserService的bean标签声明及User对象对UserService引用。

<!--对象的声明-->
<bean id="userService" class="com.test.spring.service.UserService"></bean>
 
<bean id="userAction"class="com.test.spring.action.User" >
   <property name="userservice" ref="userService"></property>
</bean>

这样配置,框架就会将UserService对象注入到User类中。

3)给list集合赋值

同样提供set方法

public class User{
    privateList<String> username;
    publicList<String> getUsername() {
        returnusername;
    }
    publicvoid setUsername(List<String> username) {
        this.username= username;
    }
}
<bean id="userAction"class="com.test.spring.action.User" >
     <propertyname="username">
           <list>
               <value>zhang,san</value>
               <value>lisi</value>
               <value>wangwu</value>                                
               </list>
    </property>
</bean>

4)给属性文件中的字段赋值

public class User{
    privateProperties props ;
    publicProperties getProps() {
        returnprops;
    }
    publicvoid setProps(Properties props) {
        this.props= props;
    }
}
<bean>
    <propertyname="props">
        <props>
           <propkey="url">jdbc:oracle:thin:@localhost:orl</prop>
           <propkey="driverName">oracle.jdbc.driver.OracleDriver</prop>
           <propkey="username">scott</prop>
           <propkey="password">tiger</prop>
        </props>
    </property>
</bean>

标签中的key值是.properties属性文件中的名称

注意:

无论给什么赋值,配置文件中标签的name属性值一定是和对象中名称一致。

3、构造方法注入

1)构造方法一个参数

public class User{
    privateString usercode;
    publicUser(String usercode) {
        this.usercode=usercode;
    }
}
<bean id="userAction"class="com.test.spring.action.User">              
    <constructor-argvalue="admin"></constructor-arg>                    
</bean>

2)构造函数有两个参数时

当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。
当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置

<constructor-argvalue="admin"index="0"></constructor-arg>                
<constructor-argvalue="23" type="int"index="1"></constructor-arg><br>

这样制定,就是构造函数中,第一个参数为string类型,第二个参数为int类型。

参考自:https://blog.csdn.net/sinat_34093604/article/details/52485270
https://blog.csdn.net/lishuangzhe7047/article/details/20740835#
https://blog.csdn.net/a909301740/article/details/78379720

spring整合基础-spring和springMVC父子容器的原理

要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的。spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

说完了spring上下文的初始化过程,这三个上下文的关系应该就了解了。如还是不太清楚,我就爱莫能助了,只能自行看代码去了。

===============================================================================================================

最近在做项目时牵扯到有关父子上下文的概念。

何为父子上下文呢?

父上下文:

使用listener监听器来加载配置文件,如下:

<listener>   

  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   

</listener>

Spring 会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

子上下文:

使用Spring MVC 来处理拦截相关的请求时,会配置DispatchServlet:

<servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet

    </servlet-class>

    <init-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>

 
<servlet-mapping>

    <servlet-name>dispatcherServlet</servlet-name>

    <url-pattern>/</url-pattern>

</servlet-mapping>

每个DispatchServlet会有一个自己的上下文,称为子上下文,它也保存在 ServletContext中,key 是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名称。当一 个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + “.CONTEXT”。

可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);

父上下文(父容器)和子上下文(子容器)的访问权限:

子上下文可以访问父上下文中的bean,但是父上下文不可以访问子上下文中的bean。

父上下文使用与否

方案一,传统型:

父上下文容器中保存数据源、服务层、DAO层、事务的Bean。

子上下文容器中保存Mvc相关的Action的Bean.

事务控制在服务层。

由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。

当然,做为“传统型”方案,也没有必要这要做。

 

方案二,激进型:

Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了—–没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。

由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。

 

 

 

Java–大项目能做好–按传统方式做,规规矩矩的做,好扩展,好维护。

Java–小项目能做快–按激进方式做,一周时间就可以出一个版本,先上线接受市场(用户)的反馈,再改进,再反馈,时间就是生命(成本)。

 

来自:https://blog.csdn.net/caomiao2006/article/details/51290494

spring特点-IOC容器启动的三种方式

前期准备工作

使用IDE:IDEA

创建项目时需要用到的框架有Java和Web

就像下图的进行勾选。

创建项目完成之后的目录

如下图所示。

创建项目时,gradle中用到的依赖:

// Java EE 通过本地Jar包导入
compile fileTree(dir:'lib',include:['*.jar'])
// Spring 框架
def springVersion = '4.3.10.RELEASE'
compile("org.springframework:spring-aop:$springVersion",
            "org.springframework:spring-orm:$springVersion",
            "org.springframework:spring-jdbc:$springVersion",
            "org.springframework:spring-instrument-tomcat:$springVersion",
            "org.springframework:spring-instrument:$springVersion",
            "org.springframework:spring-framework-bom:$springVersion",
            "org.springframework:spring-expression:$springVersion",
            "org.springframework:spring-core:$springVersion",
            "org.springframework:spring-context-support:$springVersion",
            "org.springframework:spring-context:$springVersion",
            "org.springframework:spring-beans:$springVersion",
            "org.springframework:spring-aspects:$springVersion",
            "org.springframework:spring-test:$springVersion",
            "org.springframework:spring-tx:$springVersion",
            "org.springframework:spring-web:$springVersion",
            "org.springframework:spring-webmvc:$springVersion",
            "org.springframework:spring-webmvc-portlet:$springVersion")
// 模拟测试框架
testCompile "org.mockito:mockito-core:2.+"
// 集成测试框架
testCompile 'org.testng:testng:6.8.17'
// 单元测试框架
testCompile group: 'junit', name: 'junit', version: '4.12'

还需要的框架有:Java EE

通过BeanFactory启动IOC容器

BeanFactory类是Spring框架下最核心的接口,它提供了高级的IOC配置机制。我们可以使用下面的XmlBeanDefinitionReader和DefaultListableBeanFactory类启动IOC容器。

在com.czuaphe.test包下,创建Car类

bean文件入在类路径(classpath)下

在Resource目录编写beans.xml文件生成实例Bean

然后,在test/java目录下,使用TestNG框架测试一个BeanFactoryTest测试类
通过加载Resource下的beans.xml文件启动IOC容器,得到实例Bean。

上图我们是通过类路径得到XML文件的,在gradle下,类路径就是resources目录下

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 通过类路径得到资源,以classpath:开头
Resource resource = resolver.getResource("classpath:beans.xml");

运行结果如下图所示:

bean文件放在文件系统(FileSystem)目录下

如果我们将beans.xml文件放在与Car类相同的路径下:

在test/java目录下,BeanFactoryTest类中的resouce变量只需要改变一下,就可以达到和上面一样的结果。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("file:src/main/java/com/czuaphe/test/beans.xml");

通过ApplicationContext类启动IOC容器

ApplicationContext类继承了BeanFactory类,实现了更多面向应用的功能。

我们就在上例的基础上,在test/java目录下,使用TestNG框架再编写一个ApplicationContextTest类。同样可以实现像上面两种加载XML文件启动IOC容器的方法,此外,ApplicatioinContext类还可以通过类注解的方式加载Bean启动IOC容器。

我们实现通过类注解的方式,实现启动IOC容器。首先,在com.czuaphe.test包下写Bean.java文件,编写实例化Bean的配置。

在ApplicationContextTest类下,实现通过加载类注解配置文件启动IOC容器。

另外两种通过ApplicationContext类启动IOC容器,也在注释中写出了。

通过类注解启动IOC容器,运行测试的结果如下图所示:

通过WebApplicationContext类启动IOC容器

WebApplicationContext类继承了ApplicationContext类,专门用于Web应用。启动方式也是通过Web容器启动,实现IOC容器的启动。不能简单通过Java代码实现启动。

在webapp/WEB-INF下的web.xml中定义ContextLoaderListener监听Web容器的启动,然后定义contextConfigLocation获得Spring框架的Bean配置文件。

然后在web.xml文件中,再定义一个Servlet,定义它的属性load-on-startup为1,表示在Web容器和Spring框架的IOC容器启动之后自启动,得到WebApplicationContext类的实例,就可以得到Bean实例了。

web.xml文件如下图所示

在com.czuaphe.webtest包下,编写SpringBeanServlet类,重写Servlet的init()方法,得到WebApplicationContext类的实例。

最后,对此项目配置Tomcat服务器,在启动Web容器时,就会启动Spring的IOC容器,而且会运行自定义的SpringBeanServlet,得到Bean实例。运行结果如下图所示:

总结

编写了三种IOC容器的启动方式,在非Web应用下,使用ApplicationContext启动IOC容器就已经足够使用,在Web应用下,就必须在web.xml文件中配置WebApplicationContext的启动。让Web容器启动时IOC容器也启动。BeanFactory类启动IOC容器一般不常用。

来自:https://blog.csdn.net/cuipp0509/article/details/78544497#%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E6%97%B6%E9%9C%80%E8%A6%81%E7%94%A8%E5%88%B0%E7%9A%84%E6%A1%86%E6%9E%B6%E6%9C%89java%E5%92%8Cweb

spring特点-AOP(面向切面示例)

什么是AOP?
基本概念
切面(aspect):横切关注点被模块化的特殊对象。
通知(advice):切面必须要完成的工作。切面中的每个方向称之为通知。通知是在切面对象中的。
目标(target):被通知的对象。
代理(proxy):向目标对象应用通知后创建的对象。

连接点(joinpoint):目标对象的程序执行的某个特定位置。如某个方法调用前,调用后的位置。包括两个信息:1.目标程序的哪个方法?2.方法执行
前还是执行后?
切点(pointcut):每个类会有多个连接点,AOP通过切点定位到特定的边接点。
类比,连接点相当于数所成方圆 中的记录,而切点相当于查询条件。一个切点匹配多个连接点。

 

使用XML配置的方法:

1.导入 spring包和aspectj的两个jar包

2.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test;

public interface IHuman {
    public void chifan();
    public void shuijiao();

}

实现接口的两个类:

package com.itnba.test;

import org.springframework.stereotype.Component;

public class Chinese implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("中国人吃饭");

    }
    
    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("中国人睡觉");
    }

}
package com.itnba.test;

import org.springframework.stereotype.Component;


public class American implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("美国人吃饭");

    }

    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("美国人睡觉");
    }


}

3.定义一个切面类

package com.itnba.test;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
public class Qiemian {
    
    public void chifanqian(){
        System.out.println("洗手");
    }
    public void chifanhou(){
        System.out.println("漱口");
    }
    public void shuijiaoqian(){
        System.out.println("洗澡");
        
    }

}

4.XML文件配置

<?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/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"
        default-autowire="byName"
        >
         <!-- 首相要实例化目标对象类和切面类 -->
        <bean id="chinese" class="com.itnba.test.Chinese"></bean>
        <bean id="american" class="com.itnba.test.American"></bean>
        <bean id="qiemian" class="com.itnba.test.Qiemian"></bean>
        
        <aop:config>
        <!-- 要切入的对象 -->                 
        <aop:pointcut expression="execution(* com.itnba.test.*.chifan(..))" id="chifan"/>
        <aop:pointcut expression="execution(* com.itnba.test.*.shijiao(..))" id="shijiao"/>
        <!-- 切入点 -->
        <aop:aspect id="ha" ref="qiemian"><!-- 切面类  -->
            <!--  <aop:之前before、之后after... method="切面类中的方法" pointcut-ref="上面的切入的对象"/>  -->
            <aop:before method="chifanqian()" pointcut-ref="chifan"/><!-- 之前通知 -->
            <aop:after method="chifanhou()()" pointcut-ref="chifan"/><!-- 之后通知 -->
            <aop:before method="shuijiaoqian()" pointcut-ref="shijiao"/>
        </aop:aspect>
    </aop:config>
</beans>

5.运行

package com.itnba.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //要用接口接收
        IHuman c= (IHuman)context.getBean("chinese");
        c.chifan();
        c.shuijiao();
        System.out.println("*********************************");
        //要用接口接收
        IHuman a= (IHuman) context.getBean("american");
        a.chifan();
        a.shuijiao();

    }

}

结果:(主要是第4步的xml配置中 ,引用 shuijiao  错打成了  shijiao)

使用注解的方法:

1.导入 spring包和aspectj的两个jar包

2.配置文件中加入aop命名空间

<?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/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" >

        <!-- 自动扫描包下的类,并将其实例化。多个包之间用,隔开 -->
        <context:component-scan base-package="com.itnba.test"></context:component-scan>
        <!-- 配置文件中启动AspectJ的注解功能 ,默认是false,要将其改为true -->
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 
</beans>

3.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test;

public interface IHuman {
    public void chifan();
    public void shuijiao();

}

实现接口的两个类:

package com.itnba.test;

import org.springframework.stereotype.Component;

@Component
public class Chinese implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("中国人吃饭");

    }
    
    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("中国人睡觉");
    }

}
package com.itnba.test;

import org.springframework.stereotype.Component;

@Component
public class American implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("美国人吃饭");

    }

    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("美国人睡觉");
    }


}

4.定义一个切面类,直接在里面注解

package com.itnba.test;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect //声明该类是切面类
@Component//配置文件中启动自动扫描功能
public class Qiemian {
    
    @Before("execution(* com.itnba.test.*.chifan(..))")//在那个方法的之前通知    *代表全部..代表所有形参,不管有多少
    public void chifanqian(){
        System.out.println("洗手");
    }
    @After("execution(* com.itnba.test.*.chifan(..))")//在那个方法之后通知
    public void chifanhou(){
        System.out.println("漱口");
    }
    @Before("execution(* com.itnba.test.*.shuijiao(..))")
    public void shuijiaoqian(){
        System.out.println("洗澡");
        
    }

}

5.写好后用main函数来运行一下

package com.itnba.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Chinese c= context.getBean(Chinese.class);
        c.chifan();
        c.shuijiao();
        System.out.println("*********************************");
        American a= (American) context.getBean("american");
        a.chifan();
        a.shuijiao();

    }

}

结果:

这样,Spring的面向切面编程就简单介绍完了。

通知分类:
前置通知(@Before) — 在目标方法执行前执行的通知。
后置通知(@After) — 在目标方法执行后执行的通知。无论是否发生异常。后置通知中,无法读取目标方法返回的结果。
返回通知(@AfterReturnning) –在目标方法执行成功后执行的通知。在返回通知中可以访问目标返回结果的。
@AfterReturnning(value=”execution(* com.itnba..*(..))”,returning=”result”)
public void afterRun(Object result){
System.out.println(result);
}

异常通知(@AfterThrowing) — 在目标方法执行出错后执行的通知。
@AfterThrowing(value=”execution(* com.itnba..*(..))”,throwing=”ex”)
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage());
}

环绕通知(@Around) — 需要切面方法携带ProceedingJoinPoion参数,类似于一个完整的动态代理,环绕通知必须要有一个返回值,是目标方法的返
回值。

@Around(“execution(* com.itnba..*(..))”)
public object aroundAdvice( ProceedingJoinPoint pjp){

object obj = null;
try{
//做前置通知
obj = pjp.proceed();
//做返回通知
}
catch(Exception ex){
//做异常通知
}
//做后置通知
return obj;
}

 

添加日志:

切面方法可以加入参数(JoinPoint) joinPost.getSignature().getXXX()获得相关方法信息
切面方法中可以加入Object参数,用来获得目标方法的返回值(只对返回通知起作用)

 

来自:https://www.cnblogs.com/hq233/p/6637488.html

spring特点-AOP

Spring AOP
1.1 面向切面编程基础
通常,系统由很多组件组成,每个组件负责一部分功能,然而,这些组件也经常带有一些除了 核心功能之外的附带功能 。系统服务如日志、事务管理和安全经常融入到一些其他功能模块中。这些系统服务通常叫做交叉业务,这是因为它们总是分布在系统的很多组件中。通过将这些业 务分布在多个组件中,给我们的代码引入了双重复杂性。
(1) 实现系统级业务的代码在多个组件中复制。这意味着如果你要改变这些业务逻辑,你就必须到各个模块去修改。就算把这些业务抽象成一个独立模块,其它模块只是调用它的一个方法,但是这个方法调用也还是分布在很多地方。
(2) 组件会因为那些与自己核心业务无关的代码变得杂乱。一个向地址录中添加条目的方法应该只关心如何添加地址,而不是关心它是不是安全或支持事务的。
此时,我们该怎么办呢?这正是AOP用得着的地方。AOP帮助我们将这些服务模块化,并把它们声明式地应用在需要它们的地方,使得这些组件更加专注于自身业务,完全不知道其它涉及到的系统服务。
这里的概念切面,就是我们要实现的交叉功能,是应用系统模块化的一个方面或领域。切面的 最常见例子就是日志记录。日志记录在系统中到处需要用到,利用继承来重用日志模块是不合适的,这样,就可以创建一个日志记录切面,并且使用AOP在系统中 应用。下图展示了切面应用方式

图表 1 应用切面
其中,通知Advice是切面的实际实现。连接点Joinpoint是应用程序执行过程 中插入切面的地点,这个地点可以是方法调用,异常抛出,甚至可以是要修改的字段,切面代码在这些地方插入到你的应用流程中,添加新的行为。切入点 Pointcut定义了Advice应该应用在那些连接点,通常通过指定类名和方法名,或者匹配类名和方法名式样的正则表达式来指定切入点。
1.2 AOP在Spring中的实现
基于AOP,业界 存在各种各样的AOP实现,比如,JBoss AOP、Spring AOP、ASP ectJ、 Aspect Werkz等。各自实现的功能也不一样。AOP实现的强弱在很大程度上取决于连接点模型。目前,Spring只支持方法级的连接点。这和一些其他AOP框 架不一样,如AspectJ和JBoss,它们还提供了属性接入点,这样可以防止你创建特别细致的通知,如对更新对象属性值进行拦截。然而,由于 Spring关注于提供一个实现J2EE 服务的框架,所以方法拦截可以满足大部分要求,而且Spring的观点是属性拦截破坏了封装,让Advice触发在属性值改变而不是方法调用上无疑是破坏了这个概念。
Spring的AOP框架的关键点如下:
(1)Spring实现了AOP联盟接口。在Spring AOP中,存在如下几种通知(Advice)类型
Before通知:在目标方法被调用前调用,涉及接口org.springFramework .aop.MethodBeforeAdvice;
After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。
(2)用Java 编写Spring通知,并在Spring的配置文件中,定义在什么地方应用通知的切入点。
(3)Spring的运行时通知对象。代理Bean只有在第一次被应用系统需要的时候才 被创建。如果你使用的是ApplicationContext,代理对象在BeanFactory载入所有Bean的时候被创建。Spring有两种代理 创建方式。如果目标对象实现了一个或多个接口暴露的方法,Spring将使用JDK 的 java.lang.reflect.Proxy类创建代理。这个类让Spring动态产生一个新的类,它实现所需的接口,织入了通知,并且代理对目标对 象的所有请求。如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且将 对目标对象的调用委托给这个子类。此时,需要将Spring发行包lib/cglib目录下的JAR文件发布到应用系统中。
1.3 Spring AOP的优势
借助于Spring AOP,Spring IoC能够很方便的使用到非常健壮、灵活的企业级服务,是因为Spring AOP能够提供如下几方面的优势:
(1)允许开发者使用声明式企业服务,比如事务服务、安全性服务;EJB 开发者都知道,EJB组件能够使用J2EE容器提供的声明式服务,但是这些服务要借助于EJB容器,而Spring AOP却不需要EJB容器,借助于Spring的事务抽象框架就可以这些服务。
(2)开发者可以开发满足业务需求的自定义切面;
(3)开发Spring AOP Advice很方便。因为这些AOP Advice仅是POJO类,借助于Spring提供的ProxyFactoryBean,能够快速的搭建Spring AOP Advice。