docker 공부

docker 2016. 7. 24. 05:50


* docker는 Go 로 만들어져 있다 .



* 컨테이너 명령어는 아래 github에 있다.


https://github.com/docker/docker/tree/master/api/client/container


run 커맨드는 create 커맨드와 run 커맨드를 하나로 합쳐 준 기능이다.





* 도커 이미지/컨테이너

도커 이미지는 파일, 

도커 컨테이너는 프로세스




* docker 프로세스 목록 보기

$ docker ps -a

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                    NAMES

66825389a870        hello-world         "/hello"                 40 hours ago        Exited (0) 40 hours ago                             hungry_chandrasekhar

9fb0fc1311ee        054c4599963c        "/bin/sh -c 'mkdir -p"   5 days ago          Exited (0) 5 days ago                               gigantic_jang

f7dd0d66158a        380da3b4d062        "/bin/sh -c 'yum grou"   5 days ago          Exited (0) 5 days ago                               infallible_bose

5ccaea4b7cb9        kite-dns            "sh /app/start"          22 months ago       Exited (0) 11 months ago   172.17.42.1:53->53/udp   kite-dns

e194ffa881eb        dockerfile/ghost    "/bin/bash"              23 months ago       Exited (0) 23 months ago                            prickly_archimedes

123a8a34e793        dockerfile/ghost    "bash /ghost-start"      23 months ago       Exited (0) 22 months ago   0.0.0.0:2368->2368/tcp   cranky_pasteur

e7dff68d8231        dockerfile/ghost    "bash /ghost-start"      23 months ago       Exited (0) 23 months ago                            hungry_sinoussi

41088b37ad02        dockerfile/ghost    "bash /ghost-start"      23 months ago       Exited (0) 23 months ago                            stoic_almeida

36f61ab4f3ae        dockerfile/ghost    "bash /ghost-start"      23 months ago       Exited (0) 23 months ago                            angry_curie


exited 와 up으로 대충 구분할 수 있다. exited는 종료된 것, up은 실행 중임을 의미한다.


docker ps 는 실행 중인 도커 프로세스의 목록을 출력한다.


$ docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES

76d768dbe13c        wordpress           "/entrypoint.sh apach"   About an hour ago   Up About an hour    0.0.0.0:8080->80/tcp   wordpress

a54634841ccd        mysql:5.7           "docker-entrypoint.sh"   About an hour ago   Up About an hour    3306/tcp               wordpressdb




* 도커 컨테이너를 시작하려면 docker start를 실행한다.

$ docker start wordexpress


* 도커 컨테이너를 종료하려면 docker stop를 실행한다.

$ docker stop wordexpress



* 도커 컨테이너를 삭제하려면 docker rm을 실행한다.


$ docker ps -a  | grep hello

5f615fc3de99        hello-world         "bash"                   About an hour ago   Created                                                 furious_wozniak

686db8babcdb        hello-world         "/hello"                 About an hour ago   Exited (0) About an hour ago                            evil_bohr

66825389a870        hello-world         "/hello"                 2 days ago          Exited (0) 2 days ago                                   hungry_chandrasekhar

~$ docker rm 5f615fc3de99  686db8babcdb  66825389a870

5f615fc3de99

686db8babcdb

66825389a870

~$ docker ps -a  | grep hello

// 없음





* 도커 컨테이너에 접근하려면 docker exec container_id /bin/bash를 실행한다.




* 도커 컨네이버 검색하려면 docker search 커맨드를 사용한다.

$docker search express

$docker search node

$docker search centos



* 도커 허브 로그인

$ docker login

Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.

Username:



* docker 명령어 잘 보기

docker 커맨드에 --help 잘 살펴보기



$ docker  --help

Usage: docker [OPTIONS] COMMAND [arg...]

       docker [ --help | -v | --version ]


A self-sufficient runtime for containers.


Options:


  --config=~/.docker              Location of client config files

  -D, --debug                     Enable debug mode

  -H, --host=[]                   Daemon socket(s) to connect to

  -h, --help                      Print usage

  -l, --log-level=info            Set the logging level

  --tls                           Use TLS; implied by --tlsverify

  --tlscacert=~/.docker/ca.pem    Trust certs signed only by this CA

  --tlscert=~/.docker/cert.pem    Path to TLS certificate file

  --tlskey=~/.docker/key.pem      Path to TLS key file

  --tlsverify                     Use TLS and verify the remote

  -v, --version                   Print version information and quit


Commands:

    attach    Attach to a running container

    build     Build an image from a Dockerfile

    commit    Create a new image from a container's changes

    cp        Copy files/folders between a container and the local filesystem

    create    Create a new container

    deploy    Create and update a stack from a Distributed Application Bundle (DAB)

    diff      Inspect changes on a container's filesystem

    events    Get real time events from the server

    exec      Run a command in a running container

    export    Export a container's filesystem as a tar archive

    history   Show the history of an image

    images    List images

    import    Import the contents from a tarball to create a filesystem image

    info      Display system-wide information

    inspect   Return low-level information on a container, image or task

    kill      Kill one or more running container

    load      Load an image from a tar archive or STDIN

    login     Log in to a Docker registry.

    logout    Log out from a Docker registry.

    logs      Fetch the logs of a container

    network   Manage Docker networks

    node      Manage Docker Swarm nodes

    pause     Pause all processes within one or more containers

    plugin    Manage Docker plugins

    port      List port mappings or a specific mapping for the container

    ps        List containers

    pull      Pull an image or a repository from a registry

    push      Push an image or a repository to a registry

    rename    Rename a container

    restart   Restart a container

    rm        Remove one or more containers

    rmi       Remove one or more images

    run       Run a command in a new container

    save      Save one or more images to a tar archive (streamed to STDOUT by default)

    search    Search the Docker Hu



$ docker login --help


Usage: docker login [OPTIONS] [SERVER]


Log in to a Docker registry.

If no server is specified, the default is defined by the daemon.


Options:

      --help              Print usage

  -p, --password string   Password

  -u, --username string   Username




$ docker run --help


Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]


Run a command in a new container


Options:

      --add-host value              Add a custom host-to-IP mapping (host:ip) (default [])

  -a, --attach value                Attach to STDIN, STDOUT or STDERR (default [])

      --blkio-weight value          Block IO (relative weight), between 10 and 1000

      --blkio-weight-device value   Block IO weight (relative device weight) (default [])

      --cap-add value               Add Linux capabilities (default [])

      --cap-drop value              Drop Linux capabilities (default [])

      --cgroup-parent string        Optional parent cgroup for the container

      --cidfile string              Write the container ID to the file

      --cpu-percent int             CPU percent (Windows only)

      --cpu-period int              Limit CPU CFS (Completely Fair Scheduler) period

      --cpu-quota int               Limit CPU CFS (Completely Fair Scheduler) quota

  -c, --cpu-shares int              CPU shares (relative weight)

      --cpuset-cpus string          CPUs in which to allow execution (0-3, 0,1)

      --cpuset-mems string          MEMs in which to allow execution (0-3, 0,1)

  -d, --detach                      Run container in background and print container ID

      --detach-keys string          Override the key sequence for detaching a container

      --device value                Add a host device to the container (default [])

      --device-read-bps value       Limit read rate (bytes per second) from a device (default [])

      --device-read-iops value      Limit read rate (IO per second) from a device (default [])

      --device-write-bps value      Limit write rate (bytes per second) to a device (default [])

      --device-write-iops value     Limit write rate (IO per second) to a device (default [])

      --disable-content-trust       Skip image verification (default true)

      --dns value                   Set custom DNS servers (default [])

      --dns-opt value               Set DNS options (default [])

      --dns-search value            Set custom DNS search domains (default [])

      --entrypoint string           Overwrite the default ENTRYPOINT of the image

  -e, --env value                   Set environment variables (default [])

      --env-file value              Read in a file of environment variables (default [])

      --expose value                Expose a port or a range of ports (default [])

      --group-add value             Add additional groups to join (default [])

      --health-cmd string           Command to run to check health

      --health-interval duration    Time between running the check

      --health-retries int          Consecutive failures needed to report unhealthy

      --health-timeout duration     Maximum time to allow one check to run

      --help                        Print usage

  -h, --hostname string             Container host name

  -i, --interactive                 Keep STDIN open even if not attached

      --io-maxbandwidth string      Maximum IO bandwidth limit for the system drive (Windows only)

      --io-maxiops uint             Maximum IOps limit for the system drive (Windows only)

      --ip string                   Container IPv4 address (e.g. 172.30.100.104)

      --ip6 string                  Container IPv6 address (e.g. 2001:db8::33)

      --ipc string                  IPC namespace to use

      --isolation string            Container isolation technology

      --kernel-memory string        Kernel memory limit

  -l, --label value                 Set meta data on a container (default [])

      --label-file value            Read in a line delimited file of labels (default [])

      --link value                  Add link to another container (default [])

      --link-local-ip value         Container IPv4/IPv6 link-local addresses (default [])

      --log-driver string           Logging driver for container

      --log-opt value               Log driver options (default [])

      --mac-address string          Container MAC address (e.g. 92:d0:c6:0a:29:33)

  -m, --memory string               Memory limit

      --memory-reservation string   Memory soft limit

      --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap

      --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)

      --name string                 Assign a name to the container

      --network string              Connect a container to a network (default "default")

      --network-alias value         Add network-scoped alias for the container (default [])

      --no-healthcheck              Disable any container-specified HEALTHCHECK

      --oom-kill-disable            Disable OOM Killer

      --oom-score-adj int           Tune host's OOM preferences (-1000 to 1000)

      --pid string                  PID namespace to use

      --pids-limit int              Tune container pids limit (set -1 for unlimited)

      --privileged                  Give extended privileges to this container

  -p, --publish value               Publish a container's port(s) to the host (default [])

  -P, --publish-all                 Publish all exposed ports to random ports

      --read-only                   Mount the container's root filesystem as read only

      --restart string              Restart policy to apply when a container exits (default "no")

      --rm                          Automatically remove the container when it exits

      --runtime string              Runtime to use for this container

      --security-opt value          Security Options (default [])

      --shm-size string             Size of /dev/shm, default value is 64MB

      --sig-proxy                   Proxy received signals to the process (default true)

      --stop-signal string          Signal to stop a container, SIGTERM by default (default "SIGTERM")

      --storage-opt value           Set storage driver options per container (default [])

      --sysctl value                Sysctl options (default map[])

      --tmpfs value                 Mount a tmpfs directory (default [])

  -t, --tty                         Allocate a pseudo-TTY

      --ulimit value                Ulimit options (default [])

  -u, --user string                 Username or UID (format: <name|uid>[:<group|gid>])

      --userns string               User namespace to use

      --uts string                  UTS namespace to use

  -v, --volume value                Bind mount a volume (default [])

      --volume-driver string        Optional volume driver for the container

      --volumes-from value          Mount volumes from the specified container(s) (default [])

  -w, --workdir string              Working directory inside the container




* 이미지를 도커 허브에 올릴 수 있다.

참고,  $ man Dockerfile


Posted by '김용환'
,




맥에서 centos 7.2를 설치하려면 vagrant가 필요하다.


https://www.vagrantup.com/downloads.html

mac 버전 다운로드를 클릭해서 vagrant_1.8.5.mg 를 다운받고 실행한다.


$ vagrant init



다음에, mac 용 virtualbox를  설치한다.
https://www.virtualbox.org/


vagrant 이미지에 centos가 있는지 확인한다.
https://atlas.hashicorp.com/boxes/search?utm_source=vagrantcloud.com&vagrantcloud=1

centos 7 다운로드하기 전에 확인한다.
https://atlas.hashicorp.com/centos/boxes/7



이제 vagrant를 이용해서 centos 7을 설치한다. libvirt로 사용한다.

$ vagrant box add  centos/7
==> box: Loading metadata for box 'centos/7'
    box: URL: https://atlas.hashicorp.com/centos/7
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) libvirt
2) virtualbox

Enter your choice: 1
==> box: Adding box 'centos/7' (v1606.01) for provider: libvirt
    box: Downloading: https://atlas.hashicorp.com/centos/boxes/7/versions/1606.01/providers/libvirt.box
==> box: Successfully added box 'centos/7' (v1606.01) for 'libvirt'!


(참고로 삭제는 ~$ vagrant box remove  centos/7 --provider=libvirt  명령을 내린다)



vi Vagrantfiles를 실행하여 다음을 수정한다.

#config.vm.box = "base" 에서 주석 처리를 삭제하고, config.vm.box = "centos/7"으로 수정한다




vagrant 이미지를 실행한다. 

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'centos/7' is up to date...


실행은 되는데, ssh 연결이 되지 않는다. 아마도 많은 ssh 키 때문에 그런 것 같기도하고.

 vagrant ssh-config 를 실행해서 IdentifyFile 옵션이 가르키는 파일을 삭제하고, 다시 vagrant ssh를 실행해 본다. (파일은 다시 생성된다)


$ vagrant ssh-config

Host default

  HostName 127.0.0.1

  User vagrant

  Port 2200

  UserKnownHostsFile /dev/null

  StrictHostKeyChecking no

  PasswordAuthentication no

  IdentityFile /Users/계정명/.vagrant/machines/default/virtualbox/private_key

  IdentitiesOnly yes

  LogLevel FATAL





그래도 연결이 되지 않는다. 정확한 문제가 발생하는지 보려면, 다음 커맨드를 실행한다. hang되는 부분을 찾아본다.

~$ ssh vagrant@127.0.0.1 -p 2222 -v
OpenSSH_6.9p1, LibreSSL 2.1.7
..
debug1: Next authentication method: gssapi-with-mic



 virtualbox에서 running 중인 centos 7 터미널화면으로 접속한다.

vagrant centos/7의 기본 계정이름과 패스워드는 vagrant/vagrant 이다. vagrant 계정으로 로그인한 후, 다음을 실행한다.

1. sudo vi /etc/ssh/sshd_config 
  GSSAPIAuthentification yes를 GSSAPIAuthentification no로 수정한다

2. sudo /sbin/service sshd restart 




이제 mac으로 돌아가서 vagrant ssh,  또는 ssh vagrant@localhost -p 2222 를 실행하면, 접속이 잘 되는지 확인할 수 있다.





Posted by '김용환'
,

[docker] docker 설치

docker 2016. 7. 22. 18:22


docker는 linux 와 맥을 포함한 다양한 운영 체제에서 동작할 수 있다. 

https://docs.docker.com/engine/installation/


맥에서는 쉽게 설치할 수 있다.

https://docs.docker.com/engine/installation/mac/

https://docs.docker.com/docker-for-mac/


순서대로 진행한다.


~$ docker --version

Docker version 1.12.0-rc4, build e4a0dbc, experimental


제대로 동작되는지 확인하기 위해 다음을 실행한다.


~$ docker run hello-world

Unable to find image 'hello-world:latest' locally

latest: Pulling from library/hello-world

c04b14da8d14: Pull complete

Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9

Status: Downloaded newer image for hello-world:latest


Hello from Docker!


This message shows that your installation appears to be working correctly.


To generate this message, Docker took the following steps:

 1. The Docker client contacted the Docker daemon.

 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.

 3. The Docker daemon created a new container from that image which runs the

    executable that produces the output you are currently reading.

 4. The Docker daemon streamed that output to the Docker client, which sent it

    to your terminal.


To try something more ambitious, you can run an Ubuntu container with:

 $ docker run -it ubuntu bash


Share images, automate workflows, and more with a free Docker Hub account:

 https://hub.docker.com


For more examples and ideas, visit:

 https://docs.docker.com/engine/userguide/



용량을 확인하려면 docker images 명령을 실행한다. 


~$ docker images | grep hello

hello-world         latest              c54a2cc56cbb        2 weeks ago         1.848 kB



Centos 7.1에서는 다음과 같이 작업한다.



$ sudo yum update

$ curl -sSL https://get.docker.com/  | sh

$ sudo usermod -aG docker 계정명

$  sudo systemctl start docker

(데몬 실행이 필요하다)


데몬을 실행하지 않으면 docker daemon을 못 찾는 다는 에러가 발생한다.


$ docker run hello-world

docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.

See 'docker run --help'.

$ docker ps

Cannot connect to the Docker daemon. Is the docker daemon running on this host?



이럴 때는 sudo systemctl start docker를 제대로 실행 안된 경우일수도 있고 chmod 권한 문제일 수도 있다.


안되면, sudo를 써본다.


$ sudo docker run hello-world

이전 결과와 동일



실행되는 것을 확인할 수 있다.



그리고, docker 데몬이 부팅 후에 자동으로 실행될 수 있도록 다음을 실행하는 것이 좋다.


$ sudo systemctl enable docker





만약 Hello from Docker! 메시지가 발생하지 않으면, 아래 커맨드를 이용해서 443 포트로 ssh 연결이 되는지를 확인해야 한다. proxy 이슈나 컴파일 이슈일 수 있다.


$ openssl s_client -connect index.docker.io:443





참고로 맥에서 vagrant 를 사용하여 centos 7를 설치할 수 있고, 해당 centos 7에서 docker를 설치할 수 있고, docker를 테스트해볼 수 있다.




* 맥 OS x에서 아래와 같은 에러가 발생한다면 제대로 실행이 안된 것이다. 


[~/temp] docker images

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?



docker앱을 설치한 후 docker를 실행한다. 그래서 데몬으로 떠 있어야 한다. 아래와 비슷하게 나오면 성공이다.



$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

sandbox       latest              b1c4b0a4a954        9 months ago        3.76 GB

centos              6.7                 fb5054b394b7        10 months ago       191 MB




Posted by '김용환'
,


도커 이미지를 삭제하는 방법은 두 가지이다. 컨테이너 삭제와 이미지 삭제이다.

참조 : https://docs.docker.com/engine/reference/commandline/rm/


* docker rmi : 이미지 삭제

* docker rm : 이미지를 포함한 이미지를 삭제



-f을 주면 강제 삭제한다.




정지된 모든 컨테이너를 삭제하려면, 다음을 실행한다.


$ docker rm $(docker ps -a -q)





도커의 특정 이미지를 삭제하려 할 때, 멈춰진 컨테이너 때문에 삭제가 안될 때가 있다. 이때 컨테이너를 삭제할 때 도움을 받을 수 있다.


$docker rmi 1ce28876c3cc

Error response from daemon: conflict: unable to delete 1ce28876c3cc (must be forced) - image is being used by stopped container 5ccaea4b7cb9



$ docker rm 5ccaea4b7cb9

e98d8053298b



$docker rmi 1ce28876c3cc

Untagged: hello-world:latest

Deleted: sha256:b77358fac48bc0d0b5e547b7b999c5e70a5fde9de9c086b47775568c8b88326d

Deleted: sha256:32ada9ef4cd3ccd536337fbcd8cdb6e026237f59db80498f83f3175176561ffb

Deleted: sha256:3d3313518f8e8d9723adfb09ea58b7a6d46e565979fda05a2073ecb30ca1e3a1

Deleted: sha256:b652ec3a27e758f30de4742156b5d096bb19c82f2dc836e96e430323ba166ffe





목록을 보면 삭제되었다. 


$ docker images

delete됨.





모두 삭제하기


$ docker system prune -a 



Posted by '김용환'
,


Spring 3.2를 사용 중인 java 7 애플리케이션을 java 8 애플리케이션으로 전환하는 내용을 소개한다.


먼저 spring 3.2.4를 사용 중인 java7 애플리케이션을 바로 java 8로 컴파일하면, 바로 에러가 발생한다.


Caused by: org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file - probably due to a new Java class file version that isn't supported yet:


; nested exception is java.lang.IllegalArgumentException
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:56)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:80)
    at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:102)
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:266)

    ... 64 more 






원인은 spring core의 asm이 java 8을 지원하지 않는다. (참고로 3.2.4 부터 spring-asm 모듈은 spring-core로 흡수되고, 사라졌다) 3.2.9부터 spring asm이 java 8 byte code을 인식할 수 있다. 하지만, fully가 아닌 best effort 수준이다. 완벽한 java 8은 spring 4부터 사용 가능하다.

(참고로 asm 모듈은 바이트 코드 분석 및 생성 역할을 한다.)


참고 : 

https://jira.spring.io/browse/SPR-11719

https://jira.spring.io/browse/SPR-11656


* 주의 사항은 지라에 잘 쓰여져 있다. 버전 이슈가 있어서 신중하게 쓸 필요가 있다. (현재는 3.2.17까지 릴리즈 된 상태이다)

With straightforward use of -target 1.8, there aren't many limitations to begin with. Spring 3.2.13 even includes the latest ASM 5.0.3 in the meantime, so it's fully up to date at that front. The only part worth noting is the AspectJ version: If you happen to be using AspectJ and in particular load-time weaving, there may be issues on Java 8 unless you upgrade to AspectJ 1.8.5; however, Spring 3.2.13 just supports AspectJ 1.7.4 - we haven't tested it with AspectJ 1.8.x at all.

Beyond that, Spring 3.2.x simply doesn't include any dedicated Java 8 support: i.e. no JSR-310 date-time, no parameter discovery via -parameters, etc. If you don't intend to rely on those features initially, that's by no means a showstopper. I would just strongly recommend an upgrade to Spring 4.x as soon as you intend to embrace Java 8 more fully. For the time being, using Spring 3.2.13 on Java 8 will get you quite far and is entirely safe for your purposes.

Note that we'll release 3.2.14 in May, as a minimal maintenance release, so that line is still actively supported for the time being. There's also a 3.2.15 release planned towards the end of this year, again designed as a very minimal maintenance release. Please be prepared that the 3.2.x line will fade out at that point, with its eventual end of life to be expected for mid/late 2016 and in all likelihood just one further maintenance release (3.2.16) to appear until then.




그 다음으로 수정한 것은 var argument 수정이었다.

java7에서는 적당히 쓸 수 있었는데. java8에서는 ambigous 한 문법이라고 문법 에러를 발생한다. 예를 들어, 


클래스에 test(String a, Object... args)와 test(String a, Message... args)는 java7에서 동작하지만, java8에서는 동작되지 않는다.






Posted by '김용환'
,



java의 reflection은 플랫폼을 구성하는 데 효율적으로 쓰일 수 있는 좋은 api이다.

spring의 util에는 ReflectionUtils를 통해 java의 reflection api를 쉽게 사용할 수 있는 api를 제공한다.



예제를 시작한다.


앞으로 사용할 예시를 위해서 사용될 import 이다.

import com.google.common.collect.Maps;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Map;



먼저, MyActivity를 정의했다. 


import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class MyActivity {
public String id;
public String content;
private String writer;
private boolean liked;

public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}



MyActivity 클래스에 ReflectionUtils의 findField, setField, getField, makeAccessible 메소드를 사용하여 내부 변수를 마음대로 바꾼다. 하지만, private 필드를 함부로 수정할 수 없기 때문에 makeAccessible 메소드를 사용한다.

MyActivity myActivity = new MyActivity();

Field idField = ReflectionUtils.findField(MyActivity.class, "id");
System.out.println("field(id) : " + idField);

Field writerField = ReflectionUtils.findField(MyActivity.class, "writer");
System.out.println("field(writer) : " + writerField);

ReflectionUtils.setField(idField, myActivity, "200001");
Object value = ReflectionUtils.getField(idField, myActivity);
System.out.println("value : " + value);

// error - Could not access method: Class org.springframework.util.ReflectionUtils can not access a member of class MyActivity with modifiers "private"
// ReflectionUtils.setField(writerField, myActivity, "Kent");

ReflectionUtils.makeAccessible(writerField);
ReflectionUtils.setField(writerField, myActivity, "Kent");


실행하면, 다음 결과를 얻을 것이다.


field(id) : public java.lang.String MyActivity.id

field(writer) : private java.lang.String MyActivity.writer

value : 200001




참고로, ReflectionUtils.makeAccessible 메소드는 java의 reflection api의 다음 api를 조금 추상화한 것 밖에 없다.

field.setAccessible(true);





예제 2는 조금 길지만, Reflection.doWithFields에 대한 예시이다. ReflectionUtils.FieldCallback()을 이용한 것이라서, 무척 괜찮은 api이다. 간단하게 field를 출력하기 위해 RelfectionUtils.FieldCallback()의 doWith 메소드와 matches 메소드의 사용 방법을 작성했다. 


accessible에 대해서 조심스럽게 써야 할 예시도 추가했으며, matches 메소드 없이도 doWith 메소드로 모두 처리할 수 있다. 




MyActivity myActivity = new MyActivity();
Map<String, Object> properties = Maps.newHashMap();
properties.put("id", "1");
properties.put("content", "sns number one service");

// 먼저 field를 필터하기 위해 new ReflectionUtils.FieldFilter를 한 후
// FieldCallback를 사용하여 필드의 값을 저장한다
ReflectionUtils.doWithFields(myActivity.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
System.out.println("field : " + field);
field.set(myActivity, properties.get(field.getName()));
}
}, new ReflectionUtils.FieldFilter() {
@Override
public boolean matches(Field field) {
return properties.containsKey(field.getName()) && Modifier.isPublic(field.getModifiers());
}
});
System.out.println(myActivity);



ReflectionUtils.doWithFields(myActivity.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
System.out.println("field : " + field);
if (field.isAccessible()) {
field.set(myActivity, properties.get(field.getName()));
}
}
});

System.out.println(myActivity);


/*
access 못할 필드에 set하면, FieldCallback를 사용할 때 에러 발생한다.
ReflectionUtils.doWithFields(myActivity.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
System.out.println("field : " + field);
field.set(myActivity, properties.get(field.getName()));
}
});


<결과>
field : public java.lang.String MyActivity.id
field : public java.lang.String MyActivity.content
field : private java.lang.String MyActivity.writer

Not allowed to access field 'writer': java.lang.IllegalAccessException: Class ReflectionTest$3 can not access a member of class MyActivity with modifiers "private"
java.lang.IllegalStateException: Not allowed to access field 'writer': java.lang.IllegalAccessException: Class ReflectionTest$3 can not access a member of class MyActivity with modifiers "private"

==>


*/

properties.put("writer", "kent");
properties.put("liked", true);

//FieldCallback를 사용할 때 에러가 발생하니, field에 access하게 만들던지, modifier로 구분한다.
ReflectionUtils.doWithFields(myActivity.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
System.out.println("field : " + field);
ReflectionUtils.makeAccessible(field);
field.set(myActivity, properties.get(field.getName()));
}
});

System.out.println(myActivity);



ReflectionUtils.doWithFields(myActivity.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
System.out.println("field : " + field);
int modifier = field.getModifiers();
if (Modifier.isPrivate(modifier) || Modifier.isFinal(modifier) || Modifier.isStatic(modifier)) {
// do nothing;
} else {
String name = field.getName();
if (name.equals("id") || name.equals("content")) {
field.set(myActivity, properties.get(field.getName()));
}
}
}
});
System.out.println(myActivity);


결과는 다음과 같다.


field : public java.lang.String MyActivity.id

field : public java.lang.String MyActivity.content

MyActivity[id=1,content=sns number one service,writer=<null>,liked=false]

field : public java.lang.String MyActivity.id

field : public java.lang.String MyActivity.content

field : private java.lang.String MyActivity.writer

field : private boolean MyActivity.liked

MyActivity[id=1,content=sns number one service,writer=<null>,liked=false]

field : public java.lang.String MyActivity.id

field : public java.lang.String MyActivity.content

field : private java.lang.String MyActivity.writer

field : private boolean MyActivity.liked

MyActivity[id=1,content=sns number one service,writer=kent,liked=true]

field : public java.lang.String MyActivity.id

field : public java.lang.String MyActivity.content

field : private java.lang.String MyActivity.writer

field : private boolean MyActivity.liked

MyActivity[id=1,content=sns number one service,writer=kent,liked=true]






또한 필드 뿐만 아니라, 필드에 붙은 Annotation도 처리할 수 있다. Comment 라는 모델과 CommentType이라는 Annotation 결합형에 대해서 ReflectionUtils 처리 코드를 살펴본다.



public class Comment {

public String id;

@CommentType("type1")
public String type;

public String content;


@CommentType("method")
public void createComment() {
System.out.println("create comment");
}
}


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CommentType {
String value();
}



Comment 객체를 생성하고, 필드에 CommentType에 annotation이 있다면, 처리하는 방식이다. AOP 나, 프레임 워크 구축 때 유용할 수 있는 포맷이다. 

Comment comment = new Comment();
comment.id="1";
comment.content="hahahah";
comment.type="comment";

ReflectionUtils.doWithFields(Comment.class, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {

if (field.getAnnotation(CommentType.class) != null) {
System.out.println("field : " + field);
}
List<Annotation> annotationList = Arrays.asList(field.getAnnotations());
for (Annotation annotation : annotationList) {
System.out.println(annotation);
}
}
});


결과는 다음과 같다.


field : public java.lang.String Comment.type

@CommentType(value=type1)

BUILD SUCCESSFUL

Total time: 2.121 se





다음은 필드가 아닌 메소드 관점으로 살펴보는 메소드, ReflectionUtils.findMethod 메소드 예시이다. 모든 method를 출력하는 예시이다.


반면, 주의해야 할 내용도 있다. 실제 없는 메소드를 호출하면, NullPointerException(NPE)가 발생할 수 있다. 



Comment comment = new Comment();
comment.id="1";
comment.content="hahahah";
comment.type="comment";

Method method = ReflectionUtils.findMethod(Comment.class, "createComment");
ReflectionUtils.invokeMethod(method, comment);

// java.lang.NullPointerException
// Method noMethod = ReflectionUtils.findMethod(Comment.class, "noMethod");
// ReflectionUtils.invokeMethod(noMethod, comment);


ReflectionUtils.doWithMethods(Comment.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
System.out.println(method.getName());
}
});



결과는 다음과 같다.


create comment

createComment

finalize

wait

wait

wait

equals

toString

hashCode

getClass

clone

registerNatives

notify

notifyAll






ReflectionUtils.doWithMethods를 사용하여 메소드에 연관된 annotation을 발견할 수 있다. 사용방식은 ReflectionUtils.doWithFields 메소드와 동일하다.


Comment comment = new Comment();
comment.id = "1";
comment.content = "hahahah";
comment.type = "comment";


ReflectionUtils.doWithMethods(Comment.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

Annotation annotation = AnnotationUtils.getAnnotation(method, CommentType.class);
if (annotation instanceof CommentType) {
System.out.println("find comment type annotation.");
System.out.println(method.getName());
}

}
});


결과는 다음과 같다.


find comment type annotation.

createComment

Posted by '김용환'
,





http://movie.naver.com/movie/bi/mi/basic.nhn?code=140712



조금은 식상할 수도 있겠지만, 10 대 사춘기를 보내는 아이들의 이야기이다. 어린이도 아니고 어른도 아니고 독립하고 싶은 아이들이 서로 마음을 서로 솔직히 얘기하며 위안을 갖는다. 


특히 모터를 통해 자동차와 집을 합친 모형이 나의 신선함을 추구하는 내 마음을 자극시킨다. ㅎㅎ

 



<좋은 대사>


엄마 : 많이 컸네. 우리 아들

아들 : 아뇨, 세상이 줄어든 거예요.



'영화를 보고' 카테고리의 다른 글

레디 플레이어 원  (0) 2018.04.04
칠드런 오브 맨  (0) 2016.10.25
주먹왕 랄프  (0) 2016.03.18
세기의 매치  (0) 2016.03.04
빅쇼트를 보고  (0) 2016.03.03
Posted by '김용환'
,